mysql - tutorial - todo sobre elasticsearch
¿Hay alguna forma de forzar el orden de ejecución de MySQL? (3)
Sé que puedo cambiar la forma en que MySQL ejecuta una consulta utilizando la palabra clave FORCE INDEX (abc)
. ¿Pero hay una manera de cambiar el orden de ejecución?
Mi consulta se ve así:
SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
AND a.busy = 1
AND b.something = 0
AND b.acolumn = 2
AND c.itemid = 123456
Tengo una clave para cada relación / restricción que uso. Si ejecuto la explicación en esta declaración, veo que mysql comienza a consultar c primero.
id select_type table type
1 SIMPLE c ref
2 SIMPLE b ref
3 SIMPLE a eq_ref
Sin embargo, sé que consultar en el orden a -> b -> c
sería más rápido (lo he probado) ¿Hay alguna forma de decirle a mysql que use un orden específico?
Actualización: así es como sé que a -> b -> c
es más rápido.
La consulta anterior tarda 1.9 segundos en completarse y devuelve 7 filas. Si cambio la consulta a
SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
AND a.busy = 1
AND b.something = 0
AND b.acolumn = 2
HAVING c.itemid = 123456
la consulta se completa en 0.01 segundos (sin usar, obtengo 10.000 filas). Sin embargo, esa no es una solución elegante porque esta consulta es un ejemplo simplificado. En el mundo real me he unido de c a otras tablas. Dado que HAVING
es un filtro que se ejecuta en todo el resultado, esto significaría que obtendría algunos registros de magnitudes más db que nescessary.
Edit2: Sólo un poco de información:
- La parte variable en esta consulta es c.itemid. Todo lo demás son valores fijos que no cambian.
- Los índices están bien configurados y mysql elige los correctos para mí
- entre ayb hay una relación 1: n (se usa el índice PRIMARY)
- entre byc hay una relación de muchos a muchos (se usa el índice IDX_ITEMID)
el punto es que mysql debería comenzar a consultar la tabla a y trabajar para ir hacia c y no al revés. Cualquier cambio para lograr eso.
Solución: No es exactamente lo que quería pero esto parece funcionar:
SELECT c.*
FROM table1 a
INNER JOIN table2 b ON a.id = b.table1_id
INNER JOIN table3 c ON b.itemid = c.itemid
WHERE a.itemtype = 1
AND a.busy = 1
AND b.something = 0
AND b.acolumn = 2
AND c.itemid = 123456
AND f.id IN (
SELECT DISTINCT table2.id FROM table1
INNER JOIN table2 ON table1.id = table2.table1_id
WHERE table1.itemtype = 1 AND table1.busy = 1)
Puedes intentar reescribir de dos maneras
- trae algo de la condición DONDE a UNIR
- Introducir subconsultas aunque no sean necesarias.
Ambas cosas podrían impactar al planificador.
Sin embargo, lo primero que debe verificar es si sus stats están actualizadas.
Puedes usar FORCE INDEX para forzar el orden de ejecución, y lo he hecho antes.
Si lo piensa, generalmente solo hay un orden en el que puede consultar las tablas para cualquier índice que elija.
En este caso, si desea que MySQL comience a consultar primero, asegúrese de que el índice que fuerza en b
sea uno que contenga b.table1_id
. MySQL solo podrá usar ese índice si ya se ha consultado primero.
Quizás necesites usar STRAIGHT_JOIN
.
http://dev.mysql.com/doc/refman/5.0/en/join.html
STRAIGHT_JOIN
es similar aJOIN
, excepto que la tabla izquierda siempre se lee antes que la tabla derecha. Esto se puede usar para aquellos (pocos) casos en los que el optimizador de combinación coloca las tablas en el orden incorrecto.