Czy należy zagnieżdżać zewnętrzne sprzężenia zależne w programie SQL Server?

9

Słyszałem mieszane informacje na ten temat i mam nadzieję na opinię kanoniczną lub ekspercką.

Jeśli mam wiele LEFT OUTER JOINs, każdy zależny od ostatniego, czy lepiej je zagnieżdżać?

Zobaczyć na dobry przykład, JOINaby MyParentzależy od JOINcelu 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]

wprowadź opis zdjęcia tutaj

W porównaniu do 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]

wprowadź opis zdjęcia tutaj

Jak pokazano powyżej, tworzą one różne plany zapytań w SS2k8

Mateusz
źródło
Lubię używać zagnieżdżonych połączeń: michaeljswart.com/2012/09/when-i-use-nested-joins Może to jednak być kwestia stylu.
Michael J Swart
@MichaelJSwart Twój blog wydaje się dyskutować tylko wtedy, gdy osoba na utrzymaniu JOINjestINNER JOIN
Matthew
1
Jak chcesz zdefiniować „lepszy”? Osobiście uważam, że pierwszy jest o wiele łatwiejszy do odczytania - mój umysł nie podskakuje, próbując odtworzyć związek. Posiadanie ON ... ONdwa razy z rzędu (nawias lub nie) jest bardzo mylące.
Aaron Bertrand
4
Kiedy nie znajduję żadnej różnicy w wydajności między dwoma sposobami zrobienia czegoś, następne pytanie zadaję sobie: jeśli zostanę potrącony autobusem lub wygrałbym dziś loterię, która wersja byłaby najłatwiejsza do zrozumienia i utrzymania przez każdego, kto jutro przejmie mój kod ?
Aaron Bertrand
1
use planWskazówka działa po przesadzeniu drugiego planu zapytanie do pierwszej, ale nie odwrotnie.
Martin Smith

Odpowiedzi:

3

To absolutnie nie jest odpowiedź kanoniczna, ale zauważyłem, że dla planów zapytania zagnieżdżonych pętli pokazanych w skrzynce SQL możliwe było zastosowanie planu z Zapytania 2 do Zapytania 1 za pomocą USE PLANpodpowiedzi, ale próba odwrotnej operacji nie powiodła się z

Procesor zapytań nie mógł wygenerować planu zapytania, ponieważ podpowiedź USE PLAN zawiera plan, którego nie można zweryfikować pod kątem legalności zapytania. Usuń lub zastąp wskazówkę UŻYJ PLANU. Aby uzyskać największe prawdopodobieństwo pomyślnego wymuszenia planu, sprawdź, czy plan podany w podpowiedzi USE PLAN jest generowany automatycznie przez SQL Server dla tego samego zapytania.

Wyłączenie reguły transformacji optymalizatora ReorderLOJN uniemożliwia także odniesienie do wcześniejszego pomyślnego planu.

Eksperymentowanie z większą ilością danych pokazuje, że SQL Server z pewnością jest w stanie przekształcić (A LOJ B) LOJ Csię również w A LOJ (B LOJ C)naturalny sposób, ale nie widziałem żadnych dowodów na to, że sytuacja jest odwrotna.

Bardzo wymyślny przypadek, w którym pierwsze zapytanie działa lepiej niż drugie

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] 

Co daje plany

wprowadź opis zdjęcia tutaj

Dla mnie zapytanie 1 miało czas 108 ms w porównaniu do 1163 ms dla zapytania 2.

Zapytanie 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

Zapytanie 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

Można więc wstępnie założyć, że pierwsza („nieoparta”) składnia jest potencjalnie korzystna, ponieważ pozwala na rozważenie większej liczby potencjalnych zleceń łączenia, ale nie przeprowadziłem wystarczająco wyczerpujących testów, aby mieć co do tego zaufanie.

Może być całkiem możliwe wymyślenie przykładów, w których Zapytanie 2 działa lepiej. Spróbuj obu i spójrz na plany wykonania.

Martin Smith
źródło
-1

nie ma takiego typu JOIN o nazwie „Nested Join”. jest to kolejna odmiana pisania JOIN dla wygody czytelności. możesz je postrzegać jako „zapytania podrzędne” wyłącznie w celu zrozumienia celu.

jeśli bardziej martwisz się o czytelność kodu, to uważam, że to indywidualny wybór, z czym można się nadawać.

A jeśli martwisz się wydajnością zapytania i w zapytaniu nie jest używana wskazówka „Force JOIN ORDER”, to nie ma znaczenia, czy zapytanie jest napisane za pomocą „Nested Join” czy All „Outer Join”. Serwer SQL wymyśla zamówienie na podstawie kosztu połączenia dwóch tabel i / lub wyniku. SQL Server wykonuje JOIN tylko między dwoma zestawami danych naraz.

w rzeczywistości wyobraź sobie, że w drugi sposób „zagnieżdżone łączenie”, jeśli SQL Server zdecyduje się wykonać drugą część, „MyChild AS c POŁĄCZENIE ZEWNĘTRZNE MyParent AS p ON p. [id] = c. [ParentId]” i tabela się stanie mieć wiersze, które zostaną odrzucone w NASTĘPNYM LEWYM ŁĄCZU. w takim przypadku serwer SQL wydał niepotrzebne zasoby na wykonanie POŁĄCZENIA ZEWNĘTRZNEGO tych dwóch i przekazanie tego wyniku do następnego POŁĄCZENIA.

możesz również wyglądać na podobne pytanie zadane i odpowiednio udzielone odpowiedzi tutaj. Zrozumienie składni „Nested join”

Anup Shah
źródło
1
Dlaczego zatem tworzą różne plany zapytań bez użycia FORCE JOIN ORDERpodpowiedzi?
Matthew
bez tej wskazówki nie możemy zagwarantować zlecenia JOIN, a ponieważ widzisz inny plan realizacji, który to potwierdza. na przykład w pierwszy sposób „używając całego Outer Join” serwer SQL może wykonywać dowolne z tych dwóch. najpierw „MyChild + MyGrandChild”, a następnie DOŁĄCZ do „MyParent”. Lub najpierw „MyChild + MyParent”, a następnie DOŁĄCZ do „MyGrandChild”.
Anup Shah,