Was bedeutet eigentlich die Position der ON-Klausel?

23

Die normale JOIN ... ON ...Syntax ist bekannt. Es ist aber auch möglich, die ONKlausel getrennt von der JOINentsprechenden zu positionieren . Dies ist etwas, das in der Praxis selten vorkommt und nicht in Tutorials zu finden ist, und ich habe keine Webressource gefunden, die überhaupt erwähnt, dass dies möglich ist.

Hier ist ein Skript zum Herumspielen:

SELECT *
INTO #widgets1
FROM (VALUES (1), (2), (3)) x(WidgetID)


SELECT *
INTO #widgets2
FROM (VALUES (1, 'SomeValue1'), (2, 'SomeValue2'), (3, 'SomeValue3')) x(WidgetID, SomeValue)

SELECT *
INTO #widgetProperties
FROM (VALUES
    (1, 'a'), (1, 'b'),
    (2, 'a'), (2, 'b'))
x(WidgetID, PropertyName)


--q1
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN #widgets2 w2 ON w2.WidgetID = w1.WidgetID
LEFT JOIN #widgetProperties wp ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b'
ORDER BY w1.WidgetID


--q2
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN #widgets2 w2 --no ON clause here
JOIN #widgetProperties wp
 ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b'
 ON w2.WidgetID = w1.WidgetID
ORDER BY w1.WidgetID


--q3
SELECT w1.WidgetID, w2.SomeValue, wp.PropertyName
FROM #widgets1 w1
LEFT JOIN (
    #widgets2 w2 --no SELECT or FROM here
    JOIN #widgetProperties wp
    ON w2.WidgetID = wp.WidgetID AND wp.PropertyName = 'b')
ON w2.WidgetID = w1.WidgetID
ORDER BY w1.WidgetID

q1 sieht normal aus. q2 und q3 haben diese ungewöhnlichen Positionen der ONKlausel.

Dieses Skript macht nicht unbedingt viel Sinn. Es fiel mir schwer, ein sinnvolles Szenario zu entwickeln.

Was bedeuten diese ungewöhnlichen Syntaxmuster? Wie ist das definiert? Mir ist aufgefallen, dass nicht alle Positionen und Ordnungen für die beiden ONKlauseln erlaubt sind. Was sind die Regeln dafür?

Auch ist es jemals eine gute Idee, Fragen wie diese zu schreiben?

boot4life
quelle

Antworten:

32

Wenn Sie sich das FROMSyntaxdiagramm der Klausel ansehen , werden Sie feststellen, dass es nur eine Stelle für die ONKlausel gibt:

<joined_table> ::= 
{
    <table_source> <join_type> <table_source> ON <search_condition> 
    ...
}

Was Sie verwirrend finden, ist eine einfache Rekursion, da <table_source>in <joined_table> oben eine andere <joined_table> sein kann:

[ FROM { <table_source> } [ ,...n ] ] 
<table_source> ::= 
{
    table_or_view_name ... 
    ...
    | <joined_table> 
    ...
}

Um Verwirrung zu vermeiden, sollten Sie in nicht offensichtlichen Fällen (wie in Ihren Beispielen) Klammern verwenden, um sie visuell zu trennen <table_sources>. Sie sind für den Abfrageparser nicht erforderlich, für den Menschen jedoch nützlich.

mustaccio
quelle
33

Es bestimmt die logischen Tabellen, die am Join beteiligt sind.

Mit einem einfachen Beispiel

SELECT w1.WidgetID,
       w2.SomeValue,
       wp.PropertyName
FROM   #widgets1 w1
       LEFT JOIN #widgets2 w2
         ON w2.WidgetID = w1.WidgetID
       JOIN #widgetProperties wp
         ON w2.WidgetID = wp.WidgetID
            AND wp.PropertyName = 'b'
ORDER  BY w1.WidgetID 

#widgets1wird extern verknüpft mit #widgets2- das Ergebnis bildet eine virtuelle Tabelle, mit der intern verknüpft wird #widgetProperties. Das Prädikat w2.WidgetID = wp.WidgetIDbedeutet, dass alle erweiterten Nullzeilen aus der anfänglichen äußeren Verknüpfung herausgefiltert werden, wodurch effektiv alle Verknüpfungen zu inneren Verknüpfungen werden.

Dies unterscheidet sich von q2 ...

SELECT w1.WidgetID,
       w2.SomeValue,
       wp.PropertyName
FROM   #widgets1 w1
       LEFT JOIN #widgets2 w2 --no ON clause here
                 JOIN #widgetProperties wp
                   ON w2.WidgetID = wp.WidgetID
                      AND wp.PropertyName = 'b'
         ON w2.WidgetID = w1.WidgetID
ORDER  BY w1.WidgetID

#widgets2ist innerlich verbunden mit #widgetProperties. Die aus dieser Verknüpfung resultierende virtuelle Tabelle ist dann die rechte Tabelle in der linken äußeren Verknüpfung#widgets1

Das gleiche Ergebnis kann durch Verwendung einer abgeleiteten Tabelle oder eines allgemeinen Tabellenausdrucks erzielt werden.

WITH VT2
     AS (SELECT w2.WidgetID,
                w2.SomeValue,
                wp.PropertyName
         FROM   #widgets2 w2 
                JOIN #widgetProperties wp
                  ON w2.WidgetID = wp.WidgetID
                     AND wp.PropertyName = 'b')
SELECT w1.WidgetID,
       VT2.SomeValue,
       VT2.PropertyName
FROM   #widgets1 w1
       LEFT JOIN VT2
         ON VT2.WidgetID = w1.WidgetID
ORDER  BY w1.WidgetID 

... oder alternativ können Sie die virtuellen Tabellen neu anordnen und RIGHT JOINstattdessen a verwenden.

SELECT w1.WidgetID,
       w2.SomeValue,
       wp.PropertyName
FROM   #widgets2 w2
       INNER JOIN #widgetProperties wp
               ON w2.WidgetID = wp.WidgetID
                  AND wp.PropertyName = 'b'
       RIGHT JOIN #widgets1 w1
               ON w2.WidgetID = w1.WidgetID
ORDER  BY w1.WidgetID 

Dies wird von Itzik Ben Gan hier abgedeckt

... die JOIN-Bedingungen müssen in einem guten Verhältnis zur Tabellenreihenfolge stehen. Wenn Sie also die Tabellen T1, T2, T3 und T4 in dieser Reihenfolge angeben und die JOIN-Bedingungen mit T1, T2 mit T3 und T3 mit T4 übereinstimmen, müssen Sie die JOIN-Bedingungen in der Reihenfolge angeben, die der Tabellenreihenfolge entgegengesetzt ist , so was:

FROM   T1
       <join_type> T2 T2
                  <join_type> T3 T3
                             <join_type> T4
                               ON T4.key = T3.key
                    ON T3.key = T2.key
         ON T2.key = T1.key 

Um diese Join-Technik auf eine andere Art und Weise zu betrachten, kann sich eine bestimmte JOIN-Bedingung nur auf die direkt darüber stehenden Tabellennamen oder auf Tabellennamen beziehen, auf die sich frühere JOIN-Bedingungen bereits bezogen und die bereits aufgelöst wurden.

Der Artikel weist jedoch eine Reihe von Ungenauigkeiten auf, siehe auch den Folgebrief von Lubor Kollar .

Martin Smith
quelle
Danke Martin, diese Antwort ist sehr hilfreich. Ich werde den anderen jedoch akzeptieren, da sein Standpunkt zur formalen Grammatik mir geholfen hat, das Problem vollständig zu verstehen. Insbesondere die "chiastische Beziehung" scheint eine falsche Vorstellung zu sein. Es ist ein Baum, keine Liste und keine umgekehrte Liste. mustaccio lieferte den Rahmen, um zu verstehen, warum Itziks Interpretation nicht ganz richtig ist.
Boot4life