Que signifie réellement la position de la clause ON?

23

La JOIN ... ON ...syntaxe normale est bien connue. Mais il est également possible de positionner la ONclause séparément de celle à JOINlaquelle elle correspond. C'est quelque chose qui est rarement vu dans la pratique, que l'on ne trouve pas dans les tutoriels et je n'ai trouvé aucune ressource Web qui mentionne même que cela est possible.

Voici un script pour jouer avec:

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 semble normal. q2 et q3 ont ces positionnements inhabituels de la ONclause.

Ce script n'a pas nécessairement beaucoup de sens. Il m'était difficile de concevoir un scénario significatif.

Que signifient donc ces modèles de syntaxe inhabituels? Comment est-ce défini? J'ai remarqué que toutes les positions et commandes pour les deux ONclauses ne sont pas autorisées. Quelles sont les règles qui régissent cela?

Est-ce également une bonne idée d'écrire des requêtes comme celle-ci?

boot4life
la source

Réponses:

32

Si vous regardez le FROMdiagramme de syntaxe de la clause, vous verrez qu'il n'y a qu'un seul endroit pour la ONclause:

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

Ce que vous trouvez déroutant est une simple récursivité, car <table_source>dans <joined_table> ci-dessus peut être un autre <joined_table>:

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

Pour éviter toute confusion, vous devez utiliser des parenthèses dans des cas non évidents (comme vos exemples) pour séparer visuellement <table_sources>; ils ne sont pas nécessaires pour l'analyseur de requête mais utiles pour les humains.

mustaccio
la source
33

Il détermine les tables logiques impliquées dans la jointure.

Avec un exemple simple

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 

#widgets1est laissé joint à l'extérieur #widgets2- le résultat de cela forme une table virtuelle qui est jointe à l'intérieur #widgetProperties. Le prédicat w2.WidgetID = wp.WidgetIDsignifie que toutes les lignes étendues nulles de la jointure externe initiale sont filtrées, ce qui rend toutes les jointures internes jointes.

Cela diffère de 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

#widgets2est joint à l'intérieur #widgetProperties. La table virtuelle résultant de cette jointure est alors la table de droite dans la jointure externe gauche sur#widgets1

Le même résultat peut être obtenu en utilisant une table dérivée ou une expression de table commune ...

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 

... Ou bien vous pouvez réorganiser les tables virtuelles et utiliser un à la RIGHT JOINplace.

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 

Ceci est couvert par Itzik Ben Gan ici

... les conditions JOIN doivent suivre une relation chiastique avec l'ordre des tables. Autrement dit, si vous spécifiez les tables T1, T2, T3 et T4 dans cet ordre et que les conditions JOIN correspondent à T1 avec T2, T2 avec T3 et T3 avec T4, vous devez spécifier les conditions JOIN dans l'ordre opposé à l'ordre des tables , comme ça:

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 

Pour examiner cette technique de jointure d'une manière différente, une condition JOIN donnée peut se référer uniquement aux noms de table juste au-dessus ou aux noms de table auxquels des conditions JOIN antérieures ont déjà fait référence et ont été résolues.

mais l'article comporte un certain nombre d'inexactitudes, voir également la lettre de suivi de Lubor Kollar .

Martin Smith
la source
Merci Martin, cette réponse est très utile. J'accepterai l'autre cependant, car c'est son point sur la grammaire formelle qui m'a aidé à bien comprendre le problème. En particulier, la "relation chiastique" semble être une fausse idée. C'est un arbre, pas une liste plus une liste inversée. mustaccio a fourni le cadre pour comprendre pourquoi l'interprétation d'Itzik n'est pas tout à fait correcte.
boot4life