Sollte ich abhängige äußere Joins in SQL Server verschachteln?


9

Ich habe gemischte Informationen dazu gehört und hoffe auf eine kanonische oder Expertenmeinung.

Wenn ich mehrere LEFT OUTER JOINs habe, die jeweils vom letzten abhängen, ist es besser, sie zu verschachteln?

Für ein erfundenes Beispiel hängt das JOINto MyParentvom JOINto ab 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]

Geben Sie hier die Bildbeschreibung ein

Im Vergleich zu 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]

Geben Sie hier die Bildbeschreibung ein

Wie oben gezeigt, erzeugen diese unterschiedliche Abfragepläne in SS2k8


Ich verwende gerne verschachtelte Joins: michaeljswart.com/2012/09/when-i-use-nested-joins Es kann jedoch eine Frage des Stils sein.
Michael J Swart

@ MichaelJSwart Ihr Blog scheint nur zu diskutieren, wenn der Abhängige JOINeinINNER JOIN
Matthew

1
Wie möchten Sie "besser" definieren? Persönlich finde ich das erste viel einfacher zu lesen - meine Gedanken hüpfen nicht herum und versuchen, die Beziehung rückgängig zu machen. Mit ON ... ONzweimal in Folge (Klammer oder nicht) ist sehr verwirrend.
Aaron Bertrand

4
Wenn ich keinen Leistungsunterschied zwischen zwei Arten finde, etwas zu tun, stelle ich mir als nächstes die Frage: Wenn ich heute Abend von einem Bus angefahren oder im Lotto gewonnen habe, welche Version würde von jedem, der morgen meinen Code übernimmt, am einfachsten verstanden und gewartet werden ?
Aaron Bertrand

1
Der use planHinweis funktioniert, wenn der zweite Abfrageplan in den ersten übertragen wird, nicht jedoch umgekehrt.
Martin Smith

Antworten:


3

Dies ist absolut keine kanonische Antwort, aber ich habe festgestellt, dass es für die in der SQL-Geige gezeigten Abfragepläne für verschachtelte Schleifen möglich war, den Plan unter Verwendung des USE PLANHinweises von Abfrage 2 auf Abfrage 1 anzuwenden, aber der Versuch der umgekehrten Operation schlägt fehl

Der Abfrageprozessor konnte keinen Abfrageplan erstellen, da der Hinweis "USE PLAN" einen Plan enthält, der nicht als für die Abfrage zulässig überprüft werden konnte. Entfernen oder ersetzen Sie den USE PLAN-Hinweis. Stellen Sie sicher, dass der im USE PLAN-Hinweis angegebene Plan von SQL Server automatisch für dieselbe Abfrage generiert wird, um die Wahrscheinlichkeit eines erfolgreichen Planerzwingens zu erhöhen.

Durch Deaktivieren der Optimierungsumwandlungsregel wird ReorderLOJN verhindert, dass der zuvor erfolgreiche Planhinweis ebenfalls erfolgreich ist.

Das Experimentieren mit größeren Datenmengen zeigt, dass SQL Server sicherlich auch (A LOJ B) LOJ Cauf A LOJ (B LOJ C)natürliche Weise transformiert werden kann , aber ich habe keine Beweise dafür gesehen, dass das Gegenteil der Fall ist.

Ein sehr ausgeklügelter Fall, in dem die erste Abfrage eine bessere Leistung erbringt als die zweite

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] 

Welches gibt Pläne

Geben Sie hier die Bildbeschreibung ein

Für mich hatte Abfrage 1 eine verstrichene Zeit von 108 ms gegenüber 1.163 ms für Abfrage 2.

Abfrage 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

Abfrage 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

Es kann daher vorläufig davon ausgegangen werden, dass die erste ("nicht verschachtelte") Syntax potenziell vorteilhaft ist, da dadurch mehr potenzielle Verknüpfungsaufträge berücksichtigt werden können, aber ich habe nicht ausführlich genug getestet, um in der Regel viel Vertrauen in diese zu haben.

Es ist durchaus möglich, Gegenbeispiele zu erstellen, bei denen Abfrage 2 eine bessere Leistung erbringt. Probieren Sie beide aus und sehen Sie sich die Ausführungspläne an.


-1

Es gibt keinen solchen JOIN-Typ namens "Nested Join". Es handelt sich um eine andere Variante des Schreibens, bei der JOIN aus Gründen der Lesbarkeit verwendet werden kann. Sie können sie nur zum Verständnis als "Unterabfragen" anzeigen.

Wenn Sie sich mehr Gedanken über die Lesbarkeit von Code machen, ist es meine Wahl, mit welcher Person sie sich beraten lassen.

Wenn Sie Bedenken hinsichtlich der Leistung der Abfrage haben und der Hinweis "JOIN ORDER erzwingen" in der Abfrage nicht verwendet wird, spielt es keine Rolle, ob die Abfrage mit "Nested Join" oder All "Outer Join" geschrieben wurde. SQL Server erstellt die Reihenfolge basierend auf den Kosten für die Verknüpfung von zwei Tabellen und / oder dem Ergebnis. SQL Server führt die Verbindung zwischen zwei Datensätzen gleichzeitig durch.

Stellen Sie sich in der Tat vor, dass auf die zweite Weise "verschachtelter Join", wenn SQL Server sich für den zweiten Teil entscheidet, "MyChild AS c LEFT OUTER JOIN MyParent AS p ON p. [id] = c. [ParentId]" und diese Tabelle auftreten Zeilen zu haben, die in NEXT LEFT JOIN verworfen werden. In diesem Fall hat SQL Server unnötige Ressourcen aufgewendet, um OUTER JOIN für diese beiden auszuführen und das Ergebnis an next JOIN zu übergeben.

Sie können hier auch ähnlich gestellte und beantwortete ähnliche Fragen sehen. Grundlegendes zur Syntax "Verschachtelter Join"


1
Warum erstellen sie dann unterschiedliche Abfragepläne, ohne den FORCE JOIN ORDERHinweis zu verwenden?
Matthew

Ohne diesen Hinweis können wir die JOIN-Reihenfolge nicht garantieren, und da Sie den unterschiedlichen Ausführungsplan sehen, der dies beweist. Zum Beispiel könnte der SQL Server auf die erste Art und Weise "Alle äußeren Verknüpfungen verwenden" eine dieser beiden Aktionen ausführen. zuerst "MyChild + MyGrandChild" und dann "MyParent" beitreten. Oder zuerst "MyChild + MyParent" und dann "MyGrandChild" beitreten.
Anup Shah
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.