Sintaxis de INNER JOIN anidada dentro de OUTER JOIN vs. resultados de la consulta

10

TLDR; Si nos fijamos en los 2 planes de ejecución, ¿hay una respuesta fácil para cuál es mejor? A propósito NO creé índices, por lo que es más fácil ver lo que está sucediendo.

Siguiendo con mi pregunta anterior, donde encontramos diferencias en el rendimiento de las consultas entre diferentes estilos de unión (es decir, anidado versus tradicional), me di cuenta de que la sintaxis anidada también modifica el comportamiento de la consulta. Considere las siguientes 2 consultas.

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

ingrese la descripción de la imagen aquí

Esto no tiene que hacer que los fabricantes se unan para incluir una fila automática con un ModelID que NO esté en la tabla Modelos.

ingrese la descripción de la imagen aquí

Usando la sintaxis tradicional, tenemos que cambiar la unión a Manufacturas a una unión externa, así ... pero esto cambia el plan de consulta.

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

ingrese la descripción de la imagen aquí

JeffInCO
fuente

Respuestas:

12

Si nos fijamos en los 2 planes de ejecución, ¿hay una respuesta fácil para cuál es mejor? A propósito NO creé índices, por lo que es más fácil ver lo que está sucediendo.

El segundo plan tiene un costo estimado más bajo, por lo que en ese sentido limitado es "mejor".

Los conjuntos de datos son tan pequeños que el optimizador no pasó mucho tiempo buscando alternativas. La primera forma de la consulta es encontrar un plan usando hash join y un spool de tabla desde el principio. El costo estimado de ese plan es tan bajo que el optimizador no se molesta en buscar algo mejor.

La segunda forma de la consulta es encontrar un plan utilizando solo uniones externas de bucles anidados al principio del proceso de búsqueda, y nuevamente el optimizador decide que el plan es lo suficientemente bueno. Sucede que se estima que este plan es más barato.

Dicho esto (como se menciona en los comentarios de la pregunta), las dos consultas no son semánticamente idénticas. Esto puede no ser importante para usted si puede garantizar que los resultados siempre serán los mismos para todos los posibles estados futuros de su base de datos, pero el optimizador no puede hacer esa suposición. Solo produce planes garantizados para producir los mismos resultados especificados por el SQL, en todas las circunstancias.

Me di cuenta de que la sintaxis anidada también modifica el comportamiento de la consulta.

La 'sintaxis anidada' es solo un aspecto de toda la especificación de sintaxis de unión ANSI. Para habilitar una especificación lógica completa para patrones de unión más complejos, la especificación permite paréntesis (opcionales) y FROMsubconsultas de cláusulas.

La consulta se puede escribir usando la misma sintaxis ANSI usando paréntesis:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

Este formulario muestra claramente que el requisito lógico es dejar la unión desde Autosel resultado de la unión interna Manufacturershasta Models. Omitir los paréntesis opcionales da la forma que llama 'anidada':

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

Esta no es una sintaxis diferente, solo está omitiendo paréntesis opcionales y formateando un poco.

Como Martin mencionó, también es posible en este caso expresar el requisito lógico usando uniones internas seguidas de una unión externa derecha:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

Los tres formularios de consulta anteriores usan la misma sintaxis de unión ANSI. Los tres también producen el mismo plan de ejecución física con el conjunto de datos proporcionado:

Plan de ejecución común

Como mencioné en mi respuesta a su pregunta anterior, las consultas que expresan exactamente el mismo requisito lógico no siempre producirán el mismo plan de ejecución. La forma de consulta lógica que prefiere usar es en gran medida una cuestión de estilo. No hay correlación entre un estilo particular y planes de consulta 'mejores' en general. En general, aconsejaría no reescribir una consulta para obtener un plan particular si la nueva consulta no es realmente idéntica desde el punto de vista lógico al original.

El estándar SQL también permite FROMsubconsultas de cláusulas, por lo que otra forma de escribir la misma especificación de consulta es:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

Usando la sintaxis tradicional, tenemos que cambiar la unión a `Fabricantes por una unión externa, así ... pero esto cambia el plan de consulta.

Esto probablemente cambia el significado de la consulta, en cuyo caso técnicamente no es una alternativa válida (pero vea el comentario de ypercube sobre su pregunta).

Los paréntesis (opcionales) en la sintaxis de unión ANSI están allí precisamente para requisitos de unión más complejos como este, por lo que no debe tener miedo de usarlos cuando sea necesario.

Paul White 9
fuente