No recomendaría usar STRAIGHT_JOIN sin una buena razón. Mi propia experiencia es que el optimizador de consultas de MySQL elige un plan de consulta deficiente con más frecuencia de lo que me gustaría, pero no con la frecuencia suficiente para que deba omitirlo en general, que es lo que haría si siempre usara STRAIGHT_JOIN.
Mi recomendación es dejar todas las consultas como JOIN regulares. Si descubre que una consulta está utilizando un plan de consulta subóptimo, le sugiero que primero intente reescribir o reestructurar un poco la consulta para ver si el optimizador elegirá un plan de consulta mejor. Además, para innodb al menos, asegúrese de que no se trate solo de que las estadísticas de su índice estén desactualizadas ( ANALIZAR TABLA ). Eso puede hacer que el optimizador elija un plan de consulta deficiente. Las sugerencias del optimizador deberían ser generalmente su último recurso.
Otra razón para no usar sugerencias de consulta es que su distribución de datos puede cambiar con el tiempo, o su selectividad de índice puede cambiar, etc. a medida que crece su tabla. Sus sugerencias de consulta que son óptimas ahora, pueden volverse subóptimas con el tiempo. Pero el optimizador no podrá adaptar el plan de consulta debido a sus sugerencias ahora desactualizadas. Se mantiene más flexible si permite que el optimizador tome las decisiones.
straight_join
.De la referencia de MySQL JOIN :
"STRAIGHT_JOIN es similar a JOIN, excepto que la tabla de la izquierda siempre se lee antes que la tabla de la derecha. Esto se puede usar para aquellos (pocos) casos en los que el optimizador de combinación coloca las tablas en el orden incorrecto".
fuente
Aquí hay un escenario que surgió recientemente en el trabajo.
Considere tres tablas, A, B, C.
A tiene 3000 filas; B tiene 300.000.000 filas; y C tiene 2000 filas.
Se definen claves externas: B (a_id), B (c_id).
Suponga que tiene una consulta similar a esta:
select a.id, c.id from a join b on b.a_id = a.id join c on c.id = b.c_id
En mi experiencia, MySQL puede optar por ir C -> B -> A en este caso. C es más pequeño que A y B es enorme, y todos son equijoins.
El problema es que MySQL no necesariamente tiene en cuenta el tamaño de la intersección entre (C.id y B.c_id) vs (A.id y B.a_id). Si la unión entre B y C devuelve tantas filas como B, entonces es una muy mala elección; si comenzar con A hubiera filtrado B a tantas filas como A, entonces habría sido una opción mucho mejor.
straight_join
podría usarse para forzar este orden así:select a.id, c.id from a straight_join b on b.a_id = a.id join c on c.id = b.c_id
Ahora
a
debe unirse antesb
.Por lo general, desea realizar las uniones en un orden que minimice el número de filas en el conjunto resultante. Entonces, comenzar con una mesa pequeña y unir de manera que la unión resultante también sea pequeña, es ideal. Las cosas van en forma de pera si se comienza con una mesa pequeña y se une a una mesa más grande y termina tan grande como la mesa grande.
Sin embargo, depende de las estadísticas. Si cambia la distribución de datos, el cálculo puede cambiar. También depende de los detalles de implementación del mecanismo de combinación.
Los peores casos que he visto para MySQL en los que todos los
straight_join
indicios de índice, excepto los obligatorios o agresivos, son consultas que paginan una gran cantidad de datos en un orden de clasificación estricto con filtrado ligero. MySQL prefiere usar índices para cualquier filtro y se une sobre ordenaciones; esto tiene sentido porque la mayoría de las personas no están tratando de ordenar toda la base de datos, sino que tienen un subconjunto limitado de filas que responden a la consulta, y ordenar un subconjunto limitado es mucho más rápido que filtrar toda la tabla, sin importar si está ordenada o no. En este caso, poner una combinación recta inmediatamente después de la tabla que tenía la columna indexada que quería ordenar en cosas fijas.fuente
straight_join
evalúa la tabla izquierda antes que la derecha. Entonces, si desea partirA -> B -> C
de mi ejemplo, la primerajoin
palabra clave podría reemplazarse constraight_join
.MySQL no es necesariamente bueno para elegir el orden de unión en consultas complejas. Al especificar una consulta compleja como straight_join, la consulta ejecuta las uniones en el orden en que se especifican. Si coloca la tabla para que sea el mínimo común denominador primero y especifique straight_join, puede mejorar el rendimiento de la consulta.
fuente
STRAIGHT_JOIN
, utilizando esta cláusula, puede controlar elJOIN
orden: qué tabla se escanea en el ciclo exterior y cuál está en el ciclo interior.fuente
Te diré por qué tuve que usar STRAIGHT_JOIN:
Por lo tanto, forcé a una de las combinaciones a ser straight_join para FORZAR que la combinación anterior se lea primero. ¡Esto impidió que MySQL cambiara el orden de ejecución y funcionó de maravilla!
fuente
En mi corta experiencia, una de las situaciones que
STRAIGHT_JOIN
ha reducido mi consulta de 30 segundos a 100 milisegundos es que la primera tabla del plan de ejecución no era la tabla que tiene el orden por columnas.-- table sales (45000000) rows -- table stores (3) rows SELECT whatever FROM sales INNER JOIN stores ON sales.storeId = stores.id ORDER BY sales.date, sales.id LIMIT 50; -- there is an index on (date, id)
SI el optimizador elige presionar
stores
primero , causaráUsing index; Using temporary; Using filesort
porquefuente
aquí el optimizador necesita un poco de ayuda diciéndole que presione
sales
primero usandofuente
Si su consulta termina con
ORDER BY... LIMIT...
, puede ser óptimo reformular la consulta para engañar al optimizador para que haga loLIMIT
antes delJOIN
.(Esta respuesta no se aplica solo a la pregunta original sobre
STRAIGHT_JOIN
, ni se aplica a todos los casos deSTRAIGHT_JOIN
).Comenzando con el ejemplo de @Accountant م , esto debería ejecutarse más rápido en la mayoría de las situaciones. (Y evita necesitar pistas).
SELECT whatever FROM ( SELECT id FROM sales ORDER BY date, id LIMIT 50 ) AS x JOIN sales ON sales.id = x.id JOIN stores ON sales.storeId = stores.id ORDER BY sales.date, sales.id;
Notas:
INDEX(date, id)
.sales
permite obtener solo 50 "lo que sea" sin tener que arrastrarlos en una mesa temporal.ORDER BY
debe repetirse en la consulta externa. (El Optimizador puede encontrar una manera de evitar hacer otro tipo).Me opongo a usar hits porque "Incluso si es más rápido hoy, puede que no sea más rápido mañana".
fuente
Sé que es un poco antiguo, pero aquí hay un escenario, he estado haciendo un script por lotes para completar una determinada tabla. En algún momento, la consulta fue muy lenta. Parece que el orden de unión era incorrecto en registros particulares:
El orden incorrecto se ejecuta durante aproximadamente 65 segundos mientras que el uso de straight_join se ejecuta en milisegundos
fuente
--use 120s, 18 million data explain SELECT DISTINCT d.taid FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t WHERE d.taid = t.taid AND t.client_version >= '21004007' AND t.utdid IS NOT NULL AND d.recommend_day = '20170403' LIMIT 0, 10000 --use 3.6s repalce by straight join explain SELECT DISTINCT d.taid FROM tvassist_recommend_list_everyday_diverse d STRAIGHT_JOIN tvassist_taid_all t on d.taid = t.taid WHERE t.client_version >= '21004007' AND d.recommend_day = '20170403' AND t.utdid IS NOT NULL LIMIT 0, 10000
fuente