2010-04-28 27 views
196

He creado el comando SQL que utiliza INNER JOIN para 9 tablas, de todos modos este comando toma mucho tiempo (más de cinco minutos). Entonces mi gente me sugirió cambiar INNER JOIN a LEFT JOIN porque el rendimiento de LEFT JOIN es mejor, en primer lugar es a pesar de lo que sé. Después de cambiar, la velocidad de consulta mejora significativamente.INNER JOIN vs LEFT JOIN rendimiento en SQL Server

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

Mi comandos SQL se parece a continuación: SELECT * FROM A INNER JOIN B ON ... INNER JOIN C ON ... INNER JOIN D y así sucesivamente

Actualización: Esta 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 
+0

¿Te proyectar cualquier atributo de 'coUOM'? Si no, puede usar una semi unión. Si es así, podría usar 'UNION' como alternativa. Publicar solo su cláusula 'FROM' es información inadecuada aquí. – onedaywhen

+0

Me lo he preguntado tan a menudo (porque veo todo el tiempo). –

+0

¿Perdió un pedido por en su breve esquema? Hace poco me enfrenté al problema de que cambiar 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 adelante como una respuesta.Parecía que todas las respuestas realmente no explicaban el caso al que me enfrentaba. –

Respuesta

314

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

(E incluso si un LEFT JOINeran más rápido en situaciones específicas debido a algunas difíciles de imaginar confluencia de factores, no es funcionalmente equivalente a un INNER JOIN, por lo que no puede ir simplemente reemplazar todas las instancias de una con el otro!)

Es muy probable que sus problemas de rendimiento se encuentren en otra parte, como por ejemplo, que no se haya indexado correctamente una clave candidata o una clave externa. 9 tablas es bastante para unir, por lo que la desaceleración literalmente podría ser casi en cualquier lugar. Si publica su esquema, es posible que podamos proporcionar más detalles.


Editar:

Profundizando en esto, yo podría pensar en una circunstancia en la cual un LEFT JOIN podría ser más rápido que un INNER JOIN, y es entonces cuando:

  • Algunos de las tablas son muy pequeña (digamos, menos de 10 filas);
  • Las tablas no tienen suficientes índices 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 este y ver el plan de ejecución, verá que la consulta INNER JOIN en efecto, cuesta más que el LEFT JOIN, porque satisface los dos criterios anteriores . Es porque SQL Server quiere hacer una coincidencia hash para el INNER JOIN, pero tiene bucles anidados para el LEFT JOIN; el primero es normalmente mucho más rápido, pero como el número de filas es tan pequeño y no hay índice para usar, la operación de hash resulta ser la parte más cara 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 en realidad más lenta. Pero increméntelo a 50 elementos, o 5000 elementos, y la versión de la lista se ralentiza, porque es O (N) vs. O (1) para la tabla hash.

Pero cambie esta consulta para estar en la columna ID en lugar de Name y verá una historia muy diferente. En ese caso, realiza bucles anidados para ambas consultas, pero la versión INNER JOIN puede reemplazar uno de los escaneos de índice agrupados por una búsqueda, lo que significa que literalmente será un orden de magnitud más rápido con una gran cantidad de filas.

Así que la conclusión es más o menos lo que mencioné varios párrafos arriba; 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 bajo las cuales SQL Server podría a veces elegir un plan de ejecución peor para un INNER JOIN que un LEFT JOIN.

+2

Hay otro escenario que puede conducir a una OUTER JOIN que se desempeña mejor que una INNER JOIN. Ver mi respuesta a continuación. – dbenham

+0

Esta es una respuesta sólida y sólida – MikeMurko

+6

Quiero señalar que básicamente no hay documentación de la base de datos para sustentar la idea de que las uniones internas y externas unen el rendimiento de manera diferente. Las uniones externas son un poco más caras que las uniones internas, debido al volumen de los datos y al tamaño del conjunto de resultados. Sin embargo, los algoritmos subyacentes (http://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. –

4

Es probable que sus problemas de rendimiento se deban a la cantidad de uniones que realiza y a si las columnas en las que se une tienen índices o no.

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

18

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

Primero sugiero reconstruir el índice y las estadísticas, y luego borrar la caché del plan de consulta solo para asegurarme de que no esté arruinando las cosas. Sin embargo, he tenido 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.

El motivo subyacente es este: Si tiene dos tablas y se une en una columna con un índice (en ambas tablas). La unión interna producirá el mismo resultado, independientemente de si realiza un bucle sobre las entradas del índice en la tabla uno y hace coincidir el índice en la tabla dos como si hiciera lo contrario: buclee las entradas en el índice en la tabla dos y haga coincidir í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 buscar la tabla con las entradas que menos coinciden (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 un escaneo de índice en la tabla uno y hacer coincidir 10 veces en la tabla dos. Lo contrario sería un escaneo 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 e índice incorrectos para repetir.

Si el optimizador elige optimizar la combinación izquierda en el orden en que se escribió, tendrá un mejor rendimiento que la unión interna.

PERO, el optimizador también puede optimizar una unión a la izquierda de manera subóptima como una semi unión izquierda. Para hacer que elija la que desea, puede usar la sugerencia de orden de fuerza.

85

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.

Al usar una combinación externa, el optimizador siempre puede soltar la tabla unida externa del plan de ejecución si las columnas de unión son PK de la tabla externa y ninguna de las columnas se selecciona de la tabla externa. Por ejemplo, SELECT A.* FROM A LEFT OUTER JOIN B ON A.KEY=B.KEY y B.KEY es la PK para B. Tanto Oracle (creo que estaba usando la versión 10) como Sql Server (utilicé 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.KEY puede o no requerir B en el plan de ejecución dependiendo de qué restricciones existen.

Si A.KEY es una clave externa que admite valores NULL que hace referencia a B.KEY, el optimizador no puede eliminar B del plan porque debe confirmar que existe una fila B por cada fila A.

Si A.KEY es una clave externa 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 solo porque el optimizador puede eliminar la tabla del plan, no significa que lo hará. SQL Server 2008 R2 NO descarta B del plan. Oracle 10 HACE caer B del plan. Es fácil ver cómo la combinación externa superará a la combinación interna en SQL Server en este caso.

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 esta podría ser una consideración de diseño muy importante al diseñar vistas. Con frecuencia, se construye una vista de "hacer todo" que une todo lo que un usuario podría necesitar con una tabla central. (Especialmente si hay usuarios ingenuos haciendo 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 combinaciones externas, entonces el optimizador puede (y lo hace) eliminar las tablas innecesarias del plan.

Es fundamental asegurarse de que la vista que utiliza uniones externas da los resultados correctos. Como dijo Aaronaught, no puedes sustituir ciegamente a OUTER JOIN por INNER JOIN y esperar los mismos resultados. Pero hay ocasiones en que puede ser útil por razones de rendimiento cuando se usan vistas.

Una última observación: 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 EXTERIOR si también agrega la condición < FOREIGN_KEY > NO ES NULO para la cláusula where.

+2

De hecho me encontré con este problema al crear consultas extremadamente dinámicas. Me había quedado en una UNIÓN INTERNA que estaba usando y de la que no sacaba datos, y cuando la cambié a una UNIÓN IZQUIERDA (por pura curiosidad) la consulta se ejecutó más rápido. –

+0

EDITAR: aclaró las condiciones que deben existir para que el optimizador elimine la tabla unida externa del plan de ejecución. – dbenham

+0

También encontré una situación como esta, mientras creaba una consulta dinámica. La consulta central tenía 20 UNIONES IZQUIERDAS diferentes, pero al revisar el plan de ejecución fue bastante similar en el rendimiento a una consulta manuscrita que solo tenía los 2 ENLACES INTERNOS requeridos. –

13

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

Si INNER JOIN comienza la realización de tan rápido como LEFT JOIN, es porque:

  • En una consulta compuesta en su totalidad por INNER JOIN s, el orden de combinación no importa. Esto da libertad para que el optimizador de consultas ordene las uniones como lo considere oportuno, por lo que el problema podría depender del optimizador.
  • Con LEFT JOIN, ese no es el caso porque al cambiar el orden de unión se alterarán los resultados de la consulta. Esto significa que el motor debe seguir el orden de combinació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 arruinaba por completo el optimizador. Tuvimos casos en que un FORCE ORDER reduciría el tiempo de ejecución de una consulta de 5 minutos a 10 segundos.

5

He hecho una serie de comparaciones entre las uniones externas e internas izquierdas y no he podido encontrar una diferencia consistente. 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 en el tiempo (versiones de proveedores y flujo de trabajo local). No es posible crear todas las combinaciones de índices de cobertura para satisfacer las necesidades de una gran variedad de consultas y manejar datos históricos. Se han visto consultas internas que han acabado con el rendimiento del servidor porque dos tablas grandes (de millones a decenas de millones de filas) están unidas internamente, ya que tira de una gran cantidad de campos y no existe ningún índice de cobertura.

El mayor problema, sin embargo, no parece aparecer en las discusiones anteriores. Tal vez su base de datos esté bien diseñada con desencadenantes y un procesamiento de transacción bien diseñado para garantizar buenos datos. El mío frecuentemente tiene valores NULL donde no se esperan. Sí, las definiciones de la tabla podrían hacer cumplir no-Nulls, pero esa no es una opción en mi entorno.

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

Puede ser muy rápido ya que obtiene el 90% de los datos necesarios y no descubre 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.

2

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

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

Si esas 10 tablas habían sido integradas internamente juntas, el optimizador de consultas tendría que unirlas todas aunque la consulta en sí misma no necesite 7 de 10 de las tablas. Esto se debe a que las uniones internas pueden filtrar los datos, haciéndolos esenciales para el cálculo.

Si esas 10 tablas hubieran sido unidas externamente juntas, entonces el optimizador de consultas solo uniría realmente las que eran necesarias: 3 de cada 10 en este caso. Esto se debe a que las uniones ya no están filtrando 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/

+0

Su afirmación sobre "integrantes externos" es engañosa y potencialmente incorrecta. Exterior significa que los datos en el otro lado no necesitan existir, y si no sustituye a NULL. Bajo circunstancias específicas, el RDBMS puede "omitir" (ver la respuesta anterior de dbenham). SIN EMBARGO: externo vs interno puede hacer que su consulta arroje resultados radicalmente diferentes. INNER significa - dar resultados para los cuales un ítem está tanto en A & B. EXHIBIR EXTERIOR significa todos los A, y opcionalmente B si existe. Primer caso: obtienes algunas filas, en el segundo obtienes TODAS las filas. – ripvlan

+0

@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 cualquiera de los dos conjuntos devolvería el mismo conjunto de resultados. – MarredCheese

+0

Sí y - el EXTERIOR 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 el mismo resultado es justa, sin embargo, no es cierto en el caso general y específico para cada diseño de db. Y para aquellos que no estén 100% familiarizados con el álgebra relacional, podrían causarles dolor. Mi objetivo es ofrecer más información a las personas que lean este consejo y que una IZQUIERDA/DERECHA no resolverá mágicamente un problema y podría causar más problemas. Es un poder dejado para el nivel 300 :-) – ripvlan