Dois-je imbriquer les jointures externes dépendantes dans SQL Server?

9

J'ai entendu des informations mitigées à ce sujet et j'espère un avis canonique ou expert.

Si j'ai plusieurs LEFT OUTER JOINs, chacun dépendant du dernier, est-il préférable de les imbriquer?

Pour un exemple artificiel, le JOINto MyParentdépend du JOINto 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]

entrez la description de l'image ici

Comparé à 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]

entrez la description de l'image ici

Comme indiqué ci-dessus, ils produisent différents plans de requête dans SS2k8

Matthieu
la source
J'aime utiliser les jointures imbriquées: michaeljswart.com/2012/09/when-i-use-nested-joins Cela peut être une question de style.
Michael J Swart,
@MichaelJSwart votre blog ne semble discuter que lorsque la personne à charge JOINest unINNER JOIN
Matthew
1
Comment souhaitez-vous définir "mieux"? Personnellement, je trouve le premier beaucoup plus facile à lire - mon esprit ne rebondit pas pour essayer de désosser la relation. Avoir ON ... ONdeux fois de suite (entre parenthèses ou non) est très déroutant.
Aaron Bertrand
4
Quand je ne trouve aucune différence de performance entre deux façons de faire quelque chose, la prochaine question que je me pose est: si je me fais frapper par un bus ou si je gagne à la loterie ce soir, quelle version serait la plus facilement comprise et maintenue par celui qui reprend mon code demain ?
Aaron Bertrand
1
L' use planastuce fonctionne lors de la transplantation du deuxième plan de requête dans le premier, mais pas l'inverse.
Martin Smith

Réponses:

3

Ce n'est absolument pas une réponse canonique mais j'ai remarqué que pour les plans de requête de boucles imbriquées montrés dans le SQL Fiddle, il était possible d'appliquer le plan de la requête 2 à la requête 1 avec l'utilisation de l' USE PLANindice, mais la tentative d'opération inverse échoue avec

Le processeur de requêtes n'a pas pu produire de plan de requête car l'indication USE PLAN contient un plan qui n'a pas pu être vérifié pour être légal pour la requête. Supprimez ou remplacez l'indication USE PLAN. Pour une meilleure probabilité de réussite du forçage de plan, vérifiez que le plan fourni dans l'indicateur USE PLAN est généré automatiquement par SQL Server pour la même requête.

La désactivation de la règle de transformation de l'optimiseur ReorderLOJN empêche également l'indicateur de plan précédemment réussi de réussir.

Expérimenter avec de plus grandes quantités de données montre que SQL Server est certainement capable de transformer (A LOJ B) LOJ Cà A LOJ (B LOJ C)naturellement aussi bien mais je ne vois aucune preuve que l'inverse est vrai.

Un cas très artificiel où la première requête fonctionne mieux que la seconde est

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] 

Ce qui donne des plans

entrez la description de l'image ici

Pour moi, la requête 1 avait un temps écoulé de 108 ms contre 1163 ms pour la requête 2.

Requête 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

Requête 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

On peut donc supposer provisoirement que la première syntaxe ("non imbriquée") est potentiellement bénéfique car elle permet de considérer plus d'ordres de jointure potentiels, mais je n'ai pas fait de tests suffisamment exhaustifs pour avoir une grande confiance en cela en règle générale.

Il peut être tout à fait possible de trouver des contre-exemples où Query 2 fonctionne mieux. Essayez les deux et regardez les plans d'exécution.

Martin Smith
la source
-1

il n'y a pas de type JOIN appelé "Jointure imbriquée". il s'agit d'une autre variante de l'écriture que JOIN peut être à des fins de lisibilité. vous pouvez les voir comme des "sous-requêtes" à des fins de compréhension uniquement.

si vous êtes plus préoccupé par la lisibilité du code, alors mon avis est que c'est le choix de l'individu avec lequel il peut se conférer.

Et si vous êtes préoccupé par les performances de la requête et que l'indication "Force JOIN ORDER" n'est pas utilisée dans la requête, alors peu importe si la requête est écrite avec "Nested Join" ou All "Outer Join". Le serveur SQL propose la commande en fonction du coût de la jonction de deux tables et / ou du résultat. SQL Server effectue le JOIN entre deux ensembles de données à la fois uniquement.

en fait, imaginez que dans la deuxième façon "jointure imbriquée" si le serveur SQL décide de faire la deuxième partie, "MyChild AS c LEFT OUTER JOIN MyParent AS p ON p. [id] = c. [ParentId]" et ces tables se produisent pour avoir des lignes qui vont être supprimées dans NEXT LEFT JOIN. dans ce cas, le serveur SQL a dépensé des ressources inutiles pour effectuer l'OUTER JOIN ces deux et transmettre ce résultat au JOIN suivant.

vous pouvez également consulter une question similaire posée et répondue de manière appropriée ici. Comprendre la syntaxe de «jointure imbriquée»

Anup Shah
la source
1
Pourquoi, alors, produisent-ils des plans de requête différents sans utiliser l' FORCE JOIN ORDERindice?
Matthew
sans cet indice, nous ne pouvons pas garantir l'ordre JOIN et comme vous voyez le plan d'exécution différent qui le prouve. par exemple, de la première manière, "en utilisant toutes les jointures externes", le serveur SQL peut faire n'importe lequel de ces deux. d'abord "MyChild + MyGrandChild" et ensuite JOIN à "MyParent". Ou d'abord "MyChild + MyParent" et ensuite JOIN à "MyGrandChild".
Anup Shah