¿Por qué mi consulta de MySQL usando una subselección se bloquea?
Pregunta
La siguiente consulta se cuelga: (aunque las subconsultas realizadas por separado están bien)
No sé cómo hacer que la tabla de explicación se vea bien. Si alguien me dice, lo limpiaré.
select
sum(grades.points)) as p,
from assignments
left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID
from assignments
left join grades using (assignmentID)
where ... grades.date <= '1255503600' AND grades.date >= '984902400'
group by assignmentID order by grades.date DESC);
Creo que el problema está en la tabla de primeros grados ... el tipo ALL con tantas filas parece ser la causa ... Todo está indexado.
Subí la tabla como una imagen. No se pudo obtener el formato correcto: http://imgur.com/AjX34.png
Un comentarista quería la cláusula where completa:
explain extended select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
where gradeID IN
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC);
Solución
Supongamos que utiliza una base de datos real (es decir, cualquier base de datos excepto MySQL, pero usaré Postgres como ejemplo) para hacer esta consulta:
SELECT * FROM ta WHERE aid IN (SELECT subquery)
una base de datos real vería la subconsulta y estimaría su número de filas:
- Si el número de filas es pequeño (por ejemplo, menos de unos pocos millones)
Ejecutaría la subconsulta, luego construiría un hash de ID en la memoria, lo que también los hace únicos, que es una característica de IN ().
Luego, si el número de filas extraídas de ta es una pequeña parte de ta, usaría un índice adecuado para tirar de las filas. O, si se selecciona una parte importante de la tabla, simplemente la escaneará por completo y buscará cada ID en el hash, que es muy rápido.
- Si, sin embargo, el recuento de filas de la subconsulta es bastante grande
La base de datos probablemente la reescribiría como una combinación de combinación, agregando un Sort + Unique a la subconsulta.
Sin embargo, estás usando MySQL. En este caso, no hará nada de esto (volverá a ejecutar la subconsulta para cada fila de su tabla), por lo que tomará 1000 años. Lo siento.
Otros consejos
Ver " La insoportable lentitud de IN " ;: http://www.artfulsoftware.com/infotree/queries.php#568
Súper desordenado, pero: (gracias por la ayuda de todos)
SELECT *
FROM grades
LEFT JOIN assignments ON grades.assignmentID = assignments.assignmentID
RIGHT JOIN (
SELECT g.gradeID
FROM assignments a
LEFT JOIN grades g
USING ( assignmentID )
WHERE a.classID = '7815'
AND (
a.type =30170
)
AND g.contactID =7141
g.points
REGEXP '^[-]?[0-9]+[-]?'
AND g.points != '-'
AND g.points != ''
AND (
g.pointsposs IS NULL
OR g.pointsposs = ''
)
AND g.date <= '1255503600'
AND g.date >= '984902400'
GROUP BY assignmentID
ORDER BY g.date DESC
) AS t1 ON t1.gradeID = grades.gradeID
Si su subconsulta funciona bien cuando se ejecuta por separado, intente usar UNIR en lugar de IN, como esto:
select count(assignments.assignmentID) as asscount, sum(TRIM(TRAILING '-' FROM grades.points)) as p, sum(assignments.points) as t
from assignments left join grades using (assignmentID)
join
(select grades.gradeID from assignments left join grades using (assignmentID) left join as_types on as_types.ID = assignments.type
where assignments.classID = '7815'
and (assignments.type = 30170 )
and grades.contactID = 7141
and grades.points REGEXP '^[-]?[0-9]+[-]?'
and grades.points != '-'
and grades.points != ''
and (grades.pointsposs IS NULL or grades.pointsposs = '')
and grades.date <= '1255503600'
AND grades.date >= '984902400'
group by assignmentID
order by grades.date DESC) using (gradeID);
Realmente no hay suficiente información para responder a tu pregunta, y has puesto una ... en medio de la cláusula where que es rara. ¿Qué tan grandes son las tablas involucradas y cuáles son los índices?
Dicho esto, si hay demasiados términos en una cláusula in, puede ver un rendimiento gravemente degradado. Reemplace el uso de in con una combinación correcta .
Para empezar, la tabla as_types en la cláusula in no se usa. Unirse a la izquierda no sirve para nada, así que deshazte de él.
Eso deja a la cláusula in teniendo solo la tabla de asignaciones y calificaciones de la consulta externa. Claramente, el lugar donde pertenecen las asignaciones de modificación en la cláusula where para la consulta externa. Debería mover todos los puntos grados = lo que sea a la cláusula on de la combinación izquierda a los grados.
La consulta es un poco difícil de seguir, pero sospecho que la subconsulta no es necesaria en absoluto. Parece que tu consulta es básicamente así:
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE gradeID IN
(
SELECT grades.gradeID
FROM assignments LEFT JOIN grades USING (assignmentID)
WHERE your_conditions = TRUE
);
Pero, no estás haciendo nada realmente sofisticado en la cláusula where de la subconsulta. Sospecho algo mas como
SELECT FOO()
FROM assignments LEFT JOIN grades USING (assignmentID)
GROUP BY groupings
WHERE your_conditions_with_some_tweaks = TRUE;
funcionaría igual de bien.
Si me falta alguna lógica clave aquí, comente de nuevo y editaré / eliminaré esta publicación.