Devo annidare i join esterni dipendenti in SQL Server?


9

Ho sentito informazioni contrastanti su questo e spero in un'opinione canonica o di esperti.

Se ho più LEFT OUTER JOINs, ognuna dipendente dall'ultima, è meglio nidificarle?

Per un esempio inventato, il JOINto MyParentdipende dal 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]

inserisci qui la descrizione dell'immagine

Rispetto a 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]

inserisci qui la descrizione dell'immagine

Come mostrato sopra, questi producono diversi piani di query in SS2k8


Mi piace usare i join nidificati: michaeljswart.com/2012/09/when-i-use-nested-joins Potrebbe essere una questione di stile.
Michael J Swart,

@MichaelJSwart il tuo blog sembra discutere solo quando il dipendente JOINè unINNER JOIN
Matteo

1
Come desideri definire "migliore"? Personalmente, trovo che il primo sia molto più facile da leggere: la mia mente non rimbalza nel tentativo di decodificare la relazione. Avere ON ... ONdue volte di seguito (parentesi o meno) è molto confuso.
Aaron Bertrand

4
Quando non trovo alcuna differenza di prestazione tra due modi di fare qualcosa, la domanda successiva che mi pongo è: se venissi investito da un autobus o vincessi alla lotteria stasera, quale versione sarebbe più facilmente comprensibile e manutenibile da chiunque assumerà il mio codice domani ?
Aaron Bertrand

1
Il use plansuggerimento funziona quando si trapianta il secondo piano di query nel primo ma non viceversa.
Martin Smith,

Risposte:


3

Questa non è assolutamente una risposta canonica, ma ho notato che per i piani di query dei loop nidificati mostrati nel violino SQL è stato possibile applicare il piano da Query 2 a Query 1 con l'uso del USE PLANsuggerimento, ma il tentativo di eseguire l'operazione inversa non riesce con

Il processore di query non è stato in grado di produrre un piano di query poiché il suggerimento USE PLAN contiene un piano che non è stato verificato essere legale per la query. Rimuovere o sostituire il suggerimento USE PLAN. Per una migliore probabilità di forzatura del piano riuscita, verificare che il piano fornito nel suggerimento USE PLAN sia generato automaticamente da SQL Server per la stessa query.

La disabilitazione della regola di trasformazione dell'ottimizzatore ReorderLOJN impedisce anche il suggerimento del piano precedentemente eseguito correttamente.

Sperimentare una maggiore quantità di dati mostra che SQL Server è sicuramente in grado di trasformarsi (A LOJ B) LOJ Cin modo A LOJ (B LOJ C)naturale, ma non ho visto alcuna prova che sia vero il contrario.

È un caso molto forzato in cui la prima query ha prestazioni migliori della seconda

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] 

Che dà piani

inserisci qui la descrizione dell'immagine

Per me la Query 1 ha avuto un tempo trascorso di 108 ms contro 1.163 ms per la Query 2.

Query 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

Query 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

Quindi si potrebbe presumere provvisoriamente che la prima sintassi ("non verificata") sia potenzialmente vantaggiosa in quanto consente di prendere in considerazione un numero maggiore di potenziali ordini di join, ma non ho svolto test sufficientemente esaustivi per avere molta fiducia in questo come regola generale.

Potrebbe essere del tutto possibile trovare esempi contrari in cui Query 2 ha prestazioni migliori. Prova entrambi e guarda i piani di esecuzione.


-1

non esiste un tale JOIN Type chiamato "Nested Join". si tratta di un'altra variante della scrittura del JOIN per motivi di praticità di leggibilità. puoi vederli come "Sottoquery" solo a scopo di comprensione.

se sei più preoccupato per la leggibilità del codice, allora la mia opinione è che è la scelta dell'individuo con cui sono conferibili.

E se ti preoccupi delle prestazioni della query, e il suggerimento "Forza JOIN ORDER" non viene utilizzato nella query, non importa se la query è scritta con "Nested Join" o All "Outer Join". Il server SQL fornisce l'ordine in base al costo di unione di due tabelle e / o risultati. SQL Server esegue il JOIN tra due set di dati alla volta.

infatti, immagina che nel secondo modo "annidamento nidificato" se il server SQL decide di eseguire la seconda parte, "MyChild AS c SINISTRA OUTER JOIN MyParent AS p ON p. [id] = c. [ParentId]" e quelle tabelle accadono per avere righe che verranno scartate in NEXT LEFT JOIN. in tal caso il server SQL ha speso risorse non necessarie per eseguire OUTER JOIN e passare questi risultati al JOIN successivo.

puoi anche guardare domande simili poste e risposte in modo appropriato qui. Comprensione della sintassi 'Nested join'


1
Perché, quindi, producono piani di query diversi senza utilizzare il FORCE JOIN ORDERsuggerimento?
Matteo,

senza quel suggerimento, non possiamo garantire l'ordine JOIN e come stai vedendo il diverso piano di esecuzione che lo dimostra. ad esempio nel primo modo "utilizzando tutto il Outer Join" il server SQL potrebbe eseguire una di queste due. prima "MyChild + MyGrandChild" e poi ISCRIVITI a "MyParent". Oppure prima "MyChild + MyParent" e poi ISCRIVITI a "MyGrandChild".
Anup Shah,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.