Rendimiento de INNER JOIN vs LEFT JOIN en SQL Server

259

Creé un comando SQL que usa INNER JOIN en 9 tablas, de todos modos este comando lleva mucho tiempo (más de cinco minutos). Así que mi gente me sugirió cambiar INNER JOIN a LEFT JOIN porque el rendimiento de LEFT JOIN es mejor, a pesar de lo que sé. Después de cambiarlo, la velocidad de consulta mejoró significativamente.

Me gustaría saber por qué LEFT JOIN es más rápido que INNER JOIN.

Mi comando SQL se ve a continuación: SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN Dy así sucesivamente

Actualización: esto es breve de mi esquema.

FROM sidisaleshdrmly a -- NOT HAVE PK AND FK
    INNER JOIN sidisalesdetmly b -- THIS TABLE ALSO HAVE NO PK AND FK
        ON a.CompanyCd = b.CompanyCd 
           AND a.SPRNo = b.SPRNo 
           AND a.SuffixNo = b.SuffixNo 
           AND a.dnno = b.dnno
    INNER JOIN exFSlipDet h -- PK = CompanyCd, FSlipNo, FSlipSuffix, FSlipLine
        ON a.CompanyCd = h.CompanyCd
           AND a.sprno = h.AcctSPRNo
    INNER JOIN exFSlipHdr c -- PK = CompanyCd, FSlipNo, FSlipSuffix
        ON c.CompanyCd = h.CompanyCd
           AND c.FSlipNo = h.FSlipNo 
           AND c.FSlipSuffix = h.FSlipSuffix 
    INNER JOIN coMappingExpParty d -- NO PK AND FK
        ON c.CompanyCd = d.CompanyCd
           AND c.CountryCd = d.CountryCd 
    INNER JOIN coProduct e -- PK = CompanyCd, ProductSalesCd
        ON b.CompanyCd = e.CompanyCd
           AND b.ProductSalesCd = e.ProductSalesCd 
    LEFT JOIN coUOM i -- PK = UOMId
        ON h.UOMId = i.UOMId 
    INNER JOIN coProductOldInformation j -- PK = CompanyCd, BFStatus, SpecCd
        ON a.CompanyCd = j.CompanyCd
            AND b.BFStatus = j.BFStatus
            AND b.ProductSalesCd = j.ProductSalesCd
    INNER JOIN coProductGroup1 g1 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup1Cd
        ON e.ProductGroup1Cd  = g1.ProductGroup1Cd
    INNER JOIN coProductGroup2 g2 -- PK = CompanyCd, ProductCategoryCd, UsedDepartment, ProductGroup2Cd
        ON e.ProductGroup1Cd  = g2.ProductGroup1Cd
Anónimo
fuente
1
¿Proyectas algún atributo desde coUOM? Si no, puede usar una semiunión. En caso afirmativo, podría utilizarlo UNIONcomo alternativa. Publicar solo su FROMcláusula es información inadecuada aquí.
cuando el
1
Me lo he preguntado tan a menudo (porque veo todo el tiempo).
Paul Draper
1
¿Te perdiste un Order By en tu breve esquema? Hace poco me enfrenté a un problema en el que cambiar un INNER JOIN a LEFT OUTER JOIN acelera la consulta de 3 minutos a 10 segundos. Si realmente tiene Order By en su consulta, lo explicaré más a fondo como respuesta. Parecía que todas las respuestas realmente no explicaban el caso que enfrenté.
Phuah Yee Keat

Respuestas:

403

A no LEFT JOINes absolutamente más rápido que un INNER JOIN. De hecho, es más lento; por definición, una unión externa ( LEFT JOINo RIGHT JOIN) tiene que hacer todo el trabajo de un INNER JOINplus y el trabajo adicional de extender los resultados nulo. También se esperaría que devuelva más filas, aumentando aún más el tiempo total de ejecución simplemente debido al mayor tamaño del conjunto de resultados.

(¡Incluso si a LEFT JOIN fuera más rápido en situaciones específicas debido a una confluencia de factores difícil de imaginar, no es funcionalmente equivalente a un INNER JOIN, por lo que no puede simplemente reemplazar todas las instancias de una por la otra!)

Lo más probable es que sus problemas de rendimiento se encuentren en otra parte, como no tener una clave candidata o una clave externa indexada correctamente. Hay mucho que unir a 9 tablas, por lo que la desaceleración podría ser literalmente en casi cualquier lugar. Si publica su esquema, es posible que podamos brindarle más detalles.


Editar:

Al reflexionar más sobre esto, podría pensar en una circunstancia bajo la cual un LEFT JOINpodría ser más rápido que un INNER JOIN, y es cuando:

  • Algunas de las tablas son muy pequeñas (digamos, menos de 10 filas);
  • Las tablas no tienen índices suficientes para cubrir la consulta.

Considere este ejemplo:

CREATE TABLE #Test1
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test1 (ID, Name) VALUES (1, 'One')
INSERT #Test1 (ID, Name) VALUES (2, 'Two')
INSERT #Test1 (ID, Name) VALUES (3, 'Three')
INSERT #Test1 (ID, Name) VALUES (4, 'Four')
INSERT #Test1 (ID, Name) VALUES (5, 'Five')

CREATE TABLE #Test2
(
    ID int NOT NULL PRIMARY KEY,
    Name varchar(50) NOT NULL
)
INSERT #Test2 (ID, Name) VALUES (1, 'One')
INSERT #Test2 (ID, Name) VALUES (2, 'Two')
INSERT #Test2 (ID, Name) VALUES (3, 'Three')
INSERT #Test2 (ID, Name) VALUES (4, 'Four')
INSERT #Test2 (ID, Name) VALUES (5, 'Five')

SELECT *
FROM #Test1 t1
INNER JOIN #Test2 t2
ON t2.Name = t1.Name

SELECT *
FROM #Test1 t1
LEFT JOIN #Test2 t2
ON t2.Name = t1.Name

DROP TABLE #Test1
DROP TABLE #Test2

Si ejecuta esto y ve el plan de ejecución, verá que la INNER JOINconsulta realmente cuesta más que el LEFT JOIN, porque satisface los dos criterios anteriores. Esto se debe a que SQL Server quiere hacer una coincidencia hash para el INNER JOIN, pero hace bucles anidados para el LEFT JOIN; el primero es normalmente mucho más rápido, pero dado que el número de filas es muy pequeño y no hay índice para usar, la operación de hash resulta ser la parte más costosa de la consulta.

Puede ver el mismo efecto escribiendo un programa en su lenguaje de programación favorito para realizar una gran cantidad de búsquedas en una lista con 5 elementos, frente a una tabla hash con 5 elementos. Debido al tamaño, la versión de la tabla hash es realmente más lenta. Pero aumente a 50 elementos, o 5000 elementos, y la versión de la lista se ralentiza, porque es O (N) frente a O (1) para la tabla hash.

Pero cambie esta consulta para que esté en la IDcolumna en lugar de Namey verá una historia muy diferente. En ese caso, realiza bucles anidados para ambas consultas, pero la INNER JOINversión puede reemplazar uno de los escaneos de índice agrupados con una búsqueda, lo que significa que literalmente será un orden de magnitud más rápido con un gran número de filas.

Entonces, la conclusión es más o menos lo que mencioné en varios párrafos anteriores; esto es casi seguro un problema de indexación o cobertura de índice, posiblemente combinado con una o más tablas muy pequeñas. Esas son las únicas circunstancias en las que SQL Server a veces puede elegir un plan de ejecución peor para un INNER JOINque a LEFT JOIN.

Aaronaught
fuente
44
Hay otro escenario que puede llevar a que una UNIÓN EXTERNA funcione mejor que una UNIÓN INTERNA. Vea mi respuesta a continuación.
dbenham
12
Quiero señalar que, básicamente, no hay documentación de la base de datos que respalde la idea de que las uniones internas y externas se desempeñan de manera diferente. Las combinaciones externas son un poco más caras que las internas, debido al volumen de los datos y al tamaño del conjunto de resultados. Sin embargo, los algoritmos subyacentes ( msdn.microsoft.com/en-us/library/ms191426(v=sql.105).aspx ) son los mismos para ambos tipos de combinaciones. El rendimiento debe ser similar cuando devuelven cantidades similares de datos.
Gordon Linoff
3
@Aaronaught. . . Se hizo referencia a esta respuesta en un comentario que decía algo en el sentido de que "las uniones externas funcionan significativamente peor que las uniones internas". Comenté solo para asegurarme de que esta mala interpretación no se extienda.
Gordon Linoff
16
Creo que esta respuesta es engañosa en un aspecto importante: porque establece que "UNA IZQUIERDA IZQUIERDA no es absolutamente más rápida que una UNIÓN INTERNA". Esta línea no es correcta. Es teóricamente no más rápido que un INNER JOIN. NO es "absolutamente no más rápido". La pregunta es específicamente una pregunta de rendimiento. En la práctica, he visto algunos sistemas (¡por compañías muy grandes!) En los que INNER JOIN era ridículamente lento en comparación con OUTER JOIN. La teoría y la práctica son cosas muy diferentes.
David Frenkel
55
@DavidFrenkel: Eso es muy poco probable. Solicitaría ver una comparación A / B, con planes de ejecución, si cree que es posible tal discrepancia. Posiblemente esté relacionado con planes de consulta / ejecución en caché, o estadísticas incorrectas.
Aaronaught
127

Hay un escenario importante que puede llevar a que una unión externa sea más rápida que una unión interna que aún no se ha discutido.

Cuando se usa una combinación externa, el optimizador siempre es libre de eliminar la tabla externa unida del plan de ejecución si las columnas de combinación son el PK de la tabla externa, y ninguna de las columnas de la tabla externa está referenciada fuera de la propia combinación externa. Por ejemplo, SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEYB.KEY es la PK para B. Tanto Oracle (creo que estaba usando la versión 10) como Sql Server (usé 2008 R2) eliminan la tabla B del plan de ejecución.

Lo mismo no es necesariamente cierto para una unión interna: SELECT A.* FROM A INNER JOIN B ON A.KEY=B.KEYpuede o no requerir B en el plan de ejecución dependiendo de las restricciones que existan.

Si A.KEY es una clave externa anulable que hace referencia a B.KEY, entonces el optimizador no puede eliminar B del plan porque debe confirmar que existe una fila B para cada fila A.

Si A.KEY es una clave foránea obligatoria que hace referencia a B.KEY, entonces el optimizador es libre de eliminar B del plan porque las restricciones garantizan la existencia de la fila. Pero el hecho de que el optimizador pueda eliminar la tabla del plan no significa que lo hará. SQL Server 2008 R2 NO elimina B del plan. Oracle 10 SÍ deja caer B del plan. En este caso, es fácil ver cómo la combinación externa superará a la combinación interna en SQL Server.

Este es un ejemplo trivial, y no es práctico para una consulta independiente. ¿Por qué unirse a una mesa si no es necesario?

Pero esto podría ser una consideración de diseño muy importante al diseñar vistas. Con frecuencia se crea una vista de "hacer todo" que une todo lo que un usuario pueda necesitar relacionado con una tabla central. (Especialmente si hay usuarios ingenuos que realizan consultas ad-hoc que no entienden el modelo relacional) La vista puede incluir todas las columnas relevantes de muchas tablas. Pero los usuarios finales solo pueden acceder a las columnas de un subconjunto de las tablas dentro de la vista. Si las tablas se unen con uniones externas, entonces el optimizador puede (y lo hace) eliminar las tablas innecesarias del plan.

Es fundamental asegurarse de que la vista que utiliza combinaciones externas proporcione los resultados correctos. Como ha dicho Aaronaught: no puede sustituir ciegamente OUTER JOIN por INNER JOIN y esperar los mismos resultados. Pero hay momentos en que puede ser útil por razones de rendimiento al usar vistas.

Una última nota: no he probado el impacto en el rendimiento a la luz de lo anterior, pero en teoría parece que debería poder reemplazar de forma segura una UNIÓN INTERNA con una UNIÓN EXTERNA si también agrega la condición <FOREIGN_KEY> NO ES NULO a la cláusula where.

dbenham
fuente
55
De hecho, me encontré con este problema al generar consultas extremadamente dinámicas. Me había quedado en una UNIÓN INTERNA que estaba usando y de la que no extraía datos, y cuando la cambié a UNA IZQUIERDA (por curiosidad) la consulta en realidad se ejecutó más rápido.
Erik Philips
1
EDITAR: se aclararon las condiciones que deben existir para que el optimizador elimine la tabla unida externa del plan de ejecución.
dbenham
2
Una aclaración menor a su respuesta: cuando la columna de clave externa no es anulable, INNER JOIN y LEFT JOIN se vuelven semánticamente equivalentes (es decir, su cláusula WHERE sugerida es redundante); La única diferencia sería el plan de ejecución.
Douglas
2
Aunque esto muestra un ejemplo aparentemente trivial, ¡esta es una respuesta extraordinariamente perspicaz!
pbalaga
66
+1: Parece que me he encontrado con esto en algunas consultas en las que estaba usando combinaciones internas con algunas tablas muy grandes. La unión interna estaba causando un derrame en tempdb en el plan de consulta (supongo por la razón mencionada anteriormente, y mi servidor carece de la RAM para guardar todo en la memoria). Cambiar a uniones izquierdas eliminó el derrame a tempdb, resultando que algunas de mis consultas de 20-30 segundos ahora se ejecutan en fracciones de segundo. Esto es muy importante ya que la mayoría de las personas parecen asumir que las uniones internas son más rápidas.
fosplait
23

Si todo funciona como debería, PERO todos sabemos que todo no funciona como debería, especialmente cuando se trata del optimizador de consultas, el almacenamiento en caché del plan de consulta y las estadísticas.

Primero sugeriría reconstruir el índice y las estadísticas, luego borrar el caché del plan de consulta solo para asegurarme de que eso no esté arruinando las cosas. Sin embargo, he experimentado problemas incluso cuando eso está hecho.

He experimentado algunos casos en los que una unión izquierda ha sido más rápida que una unión interna.

La razón subyacente es esta: si tiene dos tablas y se une en una columna con un índice (en ambas tablas). La unión interna producirá el mismo resultado sin importar si realiza un bucle sobre las entradas en el índice en la tabla uno y coincide con el índice en la tabla dos como si hiciera lo contrario: repita las entradas en el índice en la tabla dos y coincida con el índice en la tabla uno. El problema es que cuando tiene estadísticas engañosas, el optimizador de consultas usará las estadísticas del índice para encontrar la tabla con menos entradas coincidentes (según sus otros criterios). Si tiene dos tablas con 1 millón en cada una, en la tabla uno tiene 10 filas coincidentes y en la tabla dos tiene 100000 filas coincidentes. La mejor manera sería hacer una exploración de índice en la tabla uno y hacer coincidir 10 veces en la tabla dos. Lo contrario sería una exploración de índice que recorre más de 100000 filas e intenta coincidir 100000 veces y solo 10 tienen éxito. Entonces, si las estadísticas no son correctas, el optimizador puede elegir la tabla y el índice incorrectos para realizar un bucle.

Si el optimizador elige optimizar la unión izquierda en el orden en que está escrito, funcionará mejor que la unión interna.

PERO, el optimizador también puede optimizar una unión izquierda subóptimamente como una semiunión izquierda. Para que elija el que desee, puede usar la sugerencia de orden de fuerza.

Kvasi
fuente
18

Pruebe ambas consultas (la que tiene la unión interna y la izquierda) con OPTION (FORCE ORDER)al final y publique los resultados. OPTION (FORCE ORDER)es una sugerencia de consulta que obliga al optimizador a crear el plan de ejecución con el orden de combinación que proporcionó en la consulta.

Si INNER JOINcomienza a funcionar tan rápido como sea LEFT JOIN, es porque:

  • En una consulta compuesta completamente por INNER JOINs, el orden de unión no importa. Esto le da libertad al optimizador de consultas para ordenar las uniones como mejor le parezca, por lo que el problema podría depender del optimizador.
  • Con LEFT JOIN, ese no es el caso porque cambiar el orden de unión alterará los resultados de la consulta. Esto significa que el motor debe seguir el orden de unión que proporcionó en la consulta, que podría ser mejor que el optimizado.

No sé si esto responde a su pregunta, pero una vez estuve en un proyecto que presentaba consultas muy complejas que realizaban cálculos, lo que desordenó completamente el optimizador. Tuvimos casos en los FORCE ORDERque a reduciría el tiempo de ejecución de una consulta de 5 minutos a 10 segundos.

Francisco Pires
fuente
9

He hecho una serie de comparaciones entre las uniones externas e internas izquierdas y no he podido encontrar una diferencia constante. Hay muchas variables Estoy trabajando en una base de datos de informes con miles de tablas, muchas con una gran cantidad de campos, muchos cambios a lo largo del tiempo (versiones del proveedor y flujo de trabajo local). No es posible crear todas las combinaciones de índices de cobertura para satisfacer las necesidades de una variedad tan amplia de consultas y manejar datos históricos. He visto consultas internas que matan el rendimiento del servidor porque dos tablas grandes (de millones a decenas de millones de filas) están unidas internamente, ambas extraen una gran cantidad de campos y no existe un índice de cobertura.

Sin embargo, el mayor problema no parece aparecer en las discusiones anteriores. Tal vez su base de datos esté bien diseñada con disparadores y procesamiento de transacciones bien diseñado para garantizar buenos datos. El mío frecuentemente tiene valores NULL donde no se esperan. Sí, las definiciones de tabla podrían imponer valores nulos, pero esa no es una opción en mi entorno.

Entonces, la pregunta es ... ¿diseña su consulta solo para la velocidad, una prioridad más alta para el procesamiento de transacciones que ejecuta el mismo código miles de veces por minuto. ¿O busca la precisión que proporcionará una unión externa izquierda? Recuerde que las uniones internas deben encontrar coincidencias en ambos lados, por lo que un NULL inesperado no solo eliminará datos de las dos tablas, sino posiblemente filas enteras de información. Y sucede tan bien, no hay mensajes de error.

Puede ser muy rápido ya que obtener el 90% de los datos necesarios y no descubrir que las uniones internas han eliminado silenciosamente la información. A veces, las uniones internas pueden ser más rápidas, pero no creo que nadie haga esa suposición a menos que hayan revisado el plan de ejecución. La velocidad es importante, pero la precisión es más importante.

JO
fuente
8

Es más probable que sus problemas de rendimiento se deban a la cantidad de uniones que está haciendo y si las columnas a las que se une tienen índices o no.

En el peor de los casos, podría estar haciendo fácilmente 9 escaneos de tabla completa para cada combinación.

Eddiegroves
fuente
7

Las uniones externas pueden ofrecer un rendimiento superior cuando se usan en vistas.

Supongamos que tiene una consulta que involucra una vista, y esa vista se compone de 10 tablas unidas. Digamos que su consulta solo utiliza columnas de 3 de esas 10 tablas.

Si esas 10 tablas se hubieran unido internamente, entonces el optimizador de consultas tendría que unirlas todas, aunque su consulta en sí no necesita 7 de cada 10 tablas. Esto se debe a que las uniones internas pueden filtrar los datos, haciéndolos esenciales para el cálculo.

Si esas 10 tablas se hubieran unido externamente, el optimizador de consultas solo uniría las que fueran necesarias: 3 de cada 10 en este caso. Esto se debe a que las uniones en sí mismas ya no filtran los datos y, por lo tanto, se pueden omitir las uniones no utilizadas.

Fuente: http://www.sqlservercentral.com/blogs/sql_coach/2010/07/29/poor-little-misunderstood-views/

Queso Marred
fuente
1
Su declaración sobre "unión externa" es engañosa y potencialmente incorrecta. Externo significa que los datos en el otro lado no necesitan existir, y si no sustituyen NULL. En circunstancias específicas, el RDBMS puede "omitirlos" (ver la respuesta anterior de dbenham). SIN EMBARGO: externo vs interno puede hacer que su consulta devuelva resultados radicalmente diferentes. INTERIOR significa: dar resultados para los que un artículo está en AMBOS A y B. IZQUIERDA EXTERIOR significa todo A y, opcionalmente, B si existe. Primer caso: obtienes algunas filas, en el segundo obtienes TODAS las filas.
ripvlan
1
@ripvlan Por supuesto, las uniones externas e internas no siempre son intercambiables. La pregunta original era sobre el rendimiento, lo que implica que estamos hablando de casos en los que cualquier combinación devolvería el mismo conjunto de resultados.
MarredCheese
1
Sí y - el OUTER podría causar un problema de rendimiento porque hará que se devuelvan todas las filas (más datos). Su suposición de que las consultas dan como resultado la misma salida es justa; sin embargo, no es cierto en el caso general y específica de cada diseño de base de datos. Y para aquellos que no están 100% familiarizados con el álgebra relacional podría causarles dolor. Mi punto es solo ofrecer más información a las personas que leen esto en busca de consejos y que una IZQUIERDA / DERECHA no resolverá mágicamente un problema y podría causar más problemas. Queda un poder para el nivel 300 :-)
ripvlan
2

Encontré algo interesante en el servidor SQL al verificar si las combinaciones internas son más rápidas que las combinaciones izquierdas.

Si no incluye los elementos de la tabla unida a la izquierda, en la instrucción de selección, la unión izquierda será más rápida que la misma consulta con la unión interna.

Si incluye la tabla unida a la izquierda en la instrucción select, la unión interna con la misma consulta fue igual o más rápida que la unión izquierda.

Buzzzzzzz
fuente
0

Según mis comparaciones, encuentro que tienen exactamente el mismo plan de ejecución. Hay tres escenarios:

  1. Si y cuando devuelven los mismos resultados, tienen la misma velocidad. Sin embargo, debemos tener en cuenta que no son las mismas consultas y que LEFT JOIN posiblemente devolverá más resultados (cuando no se cumplan algunas condiciones de ENCENDIDO) --- por eso suele ser más lento.

  2. Cuando la tabla principal (la primera no constante en el plan de ejecución) tiene una condición restrictiva (WHERE id =?) Y la condición ON correspondiente está en un valor NULL, la tabla "derecha" no está unida --- esto es cuando IZQUIERDA UNIRSE es más rápido.

  3. Como se discutió en el punto 1, generalmente INNER JOIN es más restrictivo y devuelve menos resultados y, por lo tanto, es más rápido.

Ambos usan (los mismos) índices.

Jiulin Teng
fuente