web-dev-qa-db-fra.com

L'ordre logique du plan d'exécution de la requête SQL avec plusieurs jointures

Je connais l'ordre logique d'exécution de la requête SQL qui est:

FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP

Que se passera-t-il s'il y a plus d'une jointure dans la requête par exemple si nous avons une requête comme celle-ci:

SELECT *
FROM user_branch T1
INNER JOIN dimcustomer2 T2
   ON T1.BRANCH_CODE = T2.BRANCH_CODE
INNER JOIN customer_guarantee T3
   ON T3.CUSTOMER_NUM = T2.CUSTOMER_NUM

quelques données d'exemple:

customer_guarantee:    CUSTOMER_NUM      BRANCH_CODE
                      -------------------------------
                           A                X
                           B                X
                           C                Y
                           D                Z



 user_branch:           USER_ID          BRANCH_CODE    
                      --------------------------------
                           U1               Y



 dimcustomer2:         CUSTOMER_NUM      BRANCH_CODE      
                      --------------------------------
                           A                Y
                           B                Y
                           C                Y
                           D                Z

Comment cela s'exécutera-t-il? Quelle jointure sera exécutée en premier? Et que se passe-t-il s'il existe différents types de jointures dans la requête? Quel serait l'ordre d'exécution des jointures dans ce cas? Merci d'avance.

4
Pantea Tourang

Une façon de déterminer l'ordre logique des jointures consiste à remplacer la première jointure interne de votre exemple par une jointure externe gauche:

SELECT *
FROM user_branch T1
LEFT  JOIN dimcustomer2 T2
   ON T1.BRANCH_CODE = T2.BRANCH_CODE
INNER JOIN customer_guarantee T3
   ON T3.CUSTOMER_NUM = T2.CUSTOMER_NUM

Supposons que certaines lignes de T1 n'a pas de correspondance dans T2. Plus précisément, supposons que ce sont les trois tableaux:

    T1                         T2                           T3

BRANCH_CODE          BRANCH_CODE  CUSTOMER_NUM          CUSTOMER_NUM
-----------          -----------  ------------          ------------
11                   11           230                   120
12                   12           235                   170
13                   15           260                   230
14                                                      235
15                                                      245
                                                        250
                                                        260
                                                        270

Il y a deux jointures ici et deux possibilités dans l'ordre dans lequel elles sont exécutées.

1. LEFT JOIN, puis INNER JOIN

Si la jointure gauche est évaluée en premier, son résultat aura des valeurs nulles dans le T2 colonnes où T1 lignes n'avaient aucune correspondance:

 T1.BRANCH_CODE T2.BARNCH_CODE T2.CUSTOMER_NUM 
 -------------- -------------- --- ------------ 
 11 11 230 
 12 12 235 
 13 (nul)(nul)
 14 (nul)(nul)
 15 15 260 

Rejoindre ce résultat plus loin avec T3 en utilisant une jointure interne à une condition qui utilise un T2 la colonne éliminera les non-correspondances - et, par conséquent, la correspondance T1 lignes, - car un null ne peut pas satisfaire la condition d'égalité de la jointure:

 T1.BRANCH_CODE T2.BARNCH_CODE T2.CUSTOMER_NUM T3.CUSTOMER_NUM 
 -------------- -------------- --------------- --------------- 
 11 11 230 230 
 12 12 235 235 
 15 15 260 260 

De cette façon, une partie de T1 lignes seront exclues du jeu de résultats final.

2. INNER JOIN, puis LEFT JOIN

Maintenant, si la jointure interne est exécutée en premier, elle produira un jeu de résultats contenant les lignes de T2 et T3 qui correspondent à la condition de la jointure interne:

 T2.BARNCH_CODE T2.CUSTOMER_NUM T3.CUSTOMER_NUM 
 -------------- --------------- - ------------- 
 11 230 230 
 12 235 235 
 15 260 260 

Lorsque cet ensemble de résultats est ensuite joint à l'extérieur à T1, T1 étant du côté extérieur, vous obtiendrez un résultat final contenant toutes les lignes de T1 et ceux du T2-T3 jointure interne correspondant à la condition de jointure externe:

 T1.BRANCH_CODE T2.BARNCH_CODE T2.CUSTOMER_NUM T3.CUSTOMER_NUM 
 -------------- -------------- --------------- --------------- 
 11 11 230 230 
 12 12 235 235 
 13 (nul)(nul)(nul)
 14 (nul)(nul)(nul)
 15 15 260 260 

Par conséquent, cette deuxième interprétation signifierait que tous les T1 les lignes doivent être présentes dans le résultat.


Étant donné que ces deux interprétations donnent des résultats si différents, il est clair qu'une seule peut être vraie. En exécutant la requête, vous verrez qu'en réalité c'est la première. Cela signifie que logiquement, les jointures sont exécutées dans l'ordre dans lequel elles sont spécifiées dans la clause FROM.

Variations de syntaxe

Notez que la conclusion ci-dessus s'applique à la syntaxe de jointure la plus conventionnelle, à savoir ceci:

FROM
  T1
  ... JOIN T2 ON ...
  ... JOIN T3 ON ...
  ...

Votre exemple correspond à ce modèle, donc la conclusion s'applique également. Cependant, il existe des variantes qui méritent d'être mentionnées lorsque notre conclusion ne s'applique pas, ou du moins pas aussi simplement.

1. Syntaxe JOIN imbriquée

Syntaxiquement, une jointure peut être spécifiée dans une autre jointure, comme ceci:

FROM
  T1
  JOIN
    T2
    JOIN T3 ON ..
  ON ...

Dans le cas ci-dessus, JOIN T2 est rencontré avant JOIN T3. Cependant, la déclaration de l'ancienne jointure n'est pas terminée à ce stade: son sous-paragraphe ON est celui à la fin et n'est évalué logiquement qu'après le JOIN T3 ON ... partie. Donc dans ce cas, T2 est joint à T3 d'abord, puis le résultat de la jointure est joint à T1.

Vous pourriez soutenez toujours que notre conclusion se tient ici, bien qu'elle ne soit pas aussi claire dans cette situation. Nous avons conclu que les jointures sont évaluées dans l'ordre où elles sont spécifié dans la clause FROM. Dans cet exemple, la première jointure que nous rencontrons lors de l'analyse de la clause FROM, n'est pas encore complètement spécifiée au moment de la seconde.

2. Mélange de jointures par virgule et de jointures conventionnelles

Avant l'introduction de la syntaxe explicite JOIN, les jointures étaient spécifiées comme ceci:

FROM
  T1,
  T2,
  T3
WHERE
  <joining conditions, filter conditions>

Ce type de jointure, parfois appelé jointure par virgule, est toujours pris en charge par la plupart, sinon toutes, les plates-formes, y compris SQL Server.

Sans condition de jointure, une jointure par virgule est essentiellement une jointure croisée. Une condition de jointure en fait une jointure interne. Vous pouvez voir, cependant, que la condition de jointure dans ce cas vient dans une clause entièrement différente, la clause WHERE.

Désormais, SQL Server vous permet de mélanger les jointures par virgule et les jointures conventionnelles dans la même clause FROM. Lorsque vous avez un mélange de jointures comme celui-ci:

FROM
  T1,
  T2
  JOIN T3 ON ... ,
  T4 ...

SQL Server évaluera chaque élément séparé par des virgules indépendamment des autres avant de les assembler tous ensemble. Donc, dans le cas ci-dessus, le T2 JOIN T3 ON ... la jointure sera évaluée avant que son résultat ne soit joint à T1 (à filtrer davantage par toute condition de jointure qui pourrait être trouvée dans la clause WHERE). Notre conclusion ne s'applique pas du tout ici. Cependant, vous pouvez voir qu'une syntaxe très différente entre en jeu dans ce cas.

Je discute de la syntaxe mixte plus en détail dans ma réponse sur Stack Overflow: L'identifiant en plusieurs parties n'a pas pu être lié .

11
Andriy M

J'ai bien peur que l'expression "exécution logique" n'ait beaucoup de sens; L'exécution des requêtes par définition est la matérialisation physique d'un jeu de résultats. Je pense que ce que vous entendez par "exécution logique" est la compilation de requêtes, la phase où la syntaxe de requête et la signification sémantique sont analysées et le plan de requête est préparé pour implémenter ladite signification sémantique.

Tables jointes1 dans une requête sont toujours évalués de gauche à droite (ou de haut en bas):

select ... from t_a                   -- evaluated first
           join t_b                   -- evaluated second
             on t_a.c1 = t_b.c3
           join t_x                   -- evaluated third
             on t_b.c4 = t_x.c5
           ...

Vous pouvez le vérifier vous-même si vous essayez de référencer dans la clause ON une colonne qui appartient à une table incluse plus tard dans la séquence d'évaluation. Cela ne parviendra pas à compiler:

select ... from t_a
           join t_b
             on t_a.c1 = t_c.c8       -- t_c is not known yet
           join t_c                   
           ...

Une fois la requête analysée, le plan d'exécution peut effectuer des jointures dans tout ordre qui maintient la sémantique de la requête. Comme le le manuel dit ,

L'ordre des sources de table après le mot clé FROM n'affecte pas le jeu de résultats renvoyé.

Ce Q&R est quelque peu lié.


1 - A joined_table la clause se compose de deux table_sources et leur clause ON correspondante.

9
mustaccio

Tous les JOIN appartiennent en fait à la clause FROM. Sémantiquement, cela ne fait aucune différence dans l'ordre d'écriture des JOIN, tant que vous maintenez les clauses ON et n'utilisez pas les clauses LEFT/RIGHT OUTER JOIN. Dit différemment, la sortie des clauses FROM et JOIN est une seule grande relation où il est clair que peu importe dans quel ordre les colonnes sont. Ceci est très important car il s'agit d'une opportunité d'optimisation importante pour l'optimiseur de base de données: il est logique d'exécuter les JOINs parmi les relations plus petites en premier si les conditions WHERE peuvent l'élaguer. De cette façon, il pourrait être plus pratique pour la base de données de mettre ces colonnes en cache tout en travaillant sur le reste de l'ensemble de résultats.

2
MauganRa