¿Debo anidar uniones externas dependientes en SQL Server?

9

He escuchado información mixta sobre esto y espero una opinión canónica o experta.

Si tengo múltiples LEFT OUTER JOINs, cada uno dependiente del último, ¿es mejor anidarlos?

Para un ejemplo artificial, el JOINto MyParentdepende del JOINto MyChild: http://sqlfiddle.com/#!3/31022/5

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    MyChild AS c
        ON c.[Id] = gc.[ParentId]
LEFT OUTER JOIN
    MyParent AS p
        ON p.[id] = c.[ParentId]

ingrese la descripción de la imagen aquí

En comparación con http://sqlfiddle.com/#!3/31022/7

SELECT
    {columns}
FROM
    MyGrandChild AS gc
LEFT OUTER JOIN
    (
    MyChild AS c            
    LEFT OUTER JOIN
        MyParent AS p
            ON p.[id] = c.[ParentId]
    )
    ON c.[Id] = gc.[ParentId]

ingrese la descripción de la imagen aquí

Como se muestra arriba, estos producen diferentes planes de consulta en SS2k8

Mateo
fuente
Me gusta usar combinaciones anidadas: michaeljswart.com/2012/09/when-i-use-nested-joins Aunque puede ser una cuestión de estilo.
Michael J Swart
@MichaelJSwart su blog solo parece discutir cuando el dependiente JOINes unINNER JOIN
Matthew
1
¿Cómo desea definir "mejor"? Personalmente, creo que el primero es mucho más fácil de leer: mi mente no se mueve tratando de hacer ingeniería inversa en la relación. Tener ON ... ONdos veces seguidas (paréntesis o no) es muy confuso.
Aaron Bertrand
44
Cuando no encuentro diferencias de rendimiento entre dos formas de hacer algo, la siguiente pregunta que me hago es: si me atropelló un autobús o gané la lotería esta noche, ¿qué versión sería más fácil de entender y mantener por quien se haga cargo de mi código mañana? ?
Aaron Bertrand
1
La use plansugerencia funciona al trasplantar el segundo plan de consulta al primero pero no al revés.
Martin Smith

Respuestas:

3

Esta no es una respuesta canónica, pero noté que para los planes de consulta de bucles anidados que se muestran en el Fiddle de SQL, era posible aplicar el plan de la Consulta 2 a la Consulta 1 con el uso de la USE PLANsugerencia, pero al intentar la operación inversa falla con

El procesador de consultas no pudo generar un plan de consulta porque la sugerencia USE PLAN contiene un plan que no pudo verificarse como legal para la consulta. Retire o reemplace la sugerencia de PLAN DE USO Para una mayor probabilidad de forzar un plan exitoso, verifique que el plan provisto en la sugerencia USE PLAN es uno generado automáticamente por SQL Server para la misma consulta.

La desactivación de la regla de transformación del optimizador ReorderLOJN evita que la sugerencia de plan previamente exitosa también tenga éxito.

Experimentar con mayores cantidades de datos muestra que SQL Server ciertamente también es capaz de transformarse (A LOJ B) LOJ Cde A LOJ (B LOJ C)forma natural, pero no vi ninguna evidencia de que lo contrario sea cierto.

Un caso muy artificial en el que la primera consulta funciona mejor que la segunda es

DROP TABLE  MyGrandChild , MyChild,  MyParent

CREATE TABLE MyParent
(Id int)

CREATE TABLE MyChild
(Id int PRIMARY KEY
,ParentId int,
Filler char(8000) NULL)

CREATE TABLE MyGrandChild
(Id int
,ParentId int)

INSERT INTO MyChild
                      (Id, ParentId)
SELECT TOP (100000) ROW_NUMBER() OVER (ORDER BY @@SPID),
                     ROW_NUMBER() OVER (ORDER BY @@SPID)    
FROM master..spt_values  v1, master..spt_values                  

INSERT INTO MyGrandChild
                      (Id, ParentId)
OUTPUT INSERTED.Id INTO MyParent
SELECT TOP (3000) Id, Id AS ParentId
FROM MyChild
ORDER BY Id

SET STATISTICS IO ON;
SET STATISTICS TIME ON;

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN MyChild AS c
         ON c.[Id] = gc.[ParentId]
       LEFT OUTER JOIN MyParent AS p
         ON p.[Id] = c.[ParentId]

SELECT gc.Id       AS gcId,
       gc.ParentId AS gcpId,
       c.Id        AS cId,
       c.ParentId  AS cpId,
       p.Id        AS pId
FROM   MyGrandChild AS gc
       LEFT OUTER JOIN( MyChild AS c
                        LEFT OUTER JOIN MyParent AS p
                          ON p.[Id] = c.[ParentId])
         ON c.[Id] = gc.[ParentId] 

Que da planes

ingrese la descripción de la imagen aquí

Para mí, la Consulta 1 tuvo un tiempo transcurrido de 108 ms frente a 1,163 ms para la Consulta 2.

Consulta 1

Table 'Worktable'. Scan count 0, logical reads 0 
Table 'MyChild'. Scan count 0, logical reads 9196
Table 'MyGrandChild'. Scan count 1, logical reads 7
Table 'MyParent'. Scan count 1, logical reads 5

Consulta 2

Table 'MyParent'. Scan count 1, logical reads 15000
Table 'MyChild'. Scan count 0, logical reads 9000 
Table 'MyGrandChild'. Scan count 1, logical reads 7

Por lo tanto, se puede suponer provisionalmente que la primera sintaxis ("no verificada") es potencialmente beneficiosa, ya que permite que se consideren más órdenes de unión potenciales, pero no he realizado pruebas lo suficientemente exhaustivas como para tener mucha confianza en esto como regla general.

Es muy posible que se presenten ejemplos contrarios en los que la consulta 2 funcione mejor. Pruebe ambos y mire los planes de ejecución.

Martin Smith
fuente
-1

no existe un tipo de JOIN llamado "Unión anidada". Es una variación más de la escritura. JOIN puede ser para fines de conveniencia de legibilidad. puede verlos como "Subconsultas" solo para comprender el propósito.

Si le preocupa más la legibilidad del código, mi opinión es que es la elección individual con qué se puede conferir.

Y si le preocupa el rendimiento de la consulta, y la sugerencia "Forzar orden de unión" no se utiliza en la consulta, no importa si la consulta se escribe con "Unión anidada" o "Unión externa". El servidor SQL presenta el orden en función del costo de unir dos tablas y / o resultados. SQL Server hace la UNIÓN entre dos conjuntos de datos a la vez solamente.

de hecho, imagine que de la segunda manera "unión anidada" si el servidor SQL decide hacer la segunda parte, "MyChild AS c IZQUIERDA EXTERIOR UNIRSE a MyParent AS p ON p. [id] = c. [ParentId]" y esas tablas suceden tener filas que se descartarán en SIGUIENTE IZQUIERDA UNIRSE. en ese caso, el servidor SQL ha gastado recursos innecesarios en hacer la UNIÓN EXTERNA a estos dos y pasar ese resultado a la siguiente UNIÓN.

También puede ver preguntas similares formuladas y respondidas adecuadamente aquí. Comprender la sintaxis de 'unión anidada'

Anup Shah
fuente
1
¿Por qué, entonces, producen diferentes planes de consulta sin usar la FORCE JOIN ORDERpista?
Mateo
sin esa sugerencia, no podemos garantizar la orden JOIN y como está viendo el plan de ejecución diferente que lo demuestra. por ejemplo, en la primera forma, "usar todas las uniones externas" del servidor SQL podría estar haciendo cualquiera de estos dos. primero "MyChild + MyGrandChild" y luego ÚNETE a "MyParent". O Primero "MyChild + MyParent" y luego ÚNETE a "MyGrandChild".
Anup Shah