web-dev-qa-db-fra.com

Syntaxe de INNER JOIN imbriquée dans OUTER JOIN par rapport aux résultats de requête

TLDR; Si vous regardez les 2 plans d'exécution, y a-t-il une réponse facile à laquelle est la meilleure? Je n'ai PAS délibérément créé d'index, il est donc plus facile de voir ce qui se passe.

Suite à ma question précédente où nous avons trouvé une différence de performances de requête entre différents styles de jointure (c'est-à-dire imbriqués vs traditionnels), j'ai réalisé que la syntaxe imbriquée modifie également le comportement de la requête. Considérez les 2 requêtes suivantes.

SELECT  a.*, m.*, n.*
FROM    dbo.Autos a
LEFT JOIN dbo.Models m
  JOIN dbo.Manufacturers n  -- <-- Nested INNER JOIN
  ON n.ManufacturerID = m.ManufacturerID
ON m.ModelID = a.ModelID

enter image description here

Cela ne doit pas obliger les fabricants à se joindre pour inclure une ligne automatique avec un ModelID qui est [~ # ~] pas [~ # ~] dans le tableau Modèles.

enter image description here

En utilisant la syntaxe traditionnelle, nous devons changer la jointure en Manufactures en une jointure externe, comme ça ... mais cela change le plan de requête.

SELECT a.*, m.*, n.*
FROM dbo.Autos a
LEFT JOIN dbo.Models m
ON m.ModelID = a.ModelID
LEFT JOIN dbo.Manufacturers n -- <-- Now LEFT OUTER JOIN
ON n.ManufacturerID = m.ManufacturerID

enter image description here

10
JeffInCO

Si vous regardez les 2 plans d'exécution, y a-t-il une réponse facile à laquelle est la meilleure? Je n'ai PAS délibérément créé d'index, il est donc plus facile de voir ce qui se passe.

Le deuxième plan a un coût estimé inférieur, donc dans ce sens limité, il est "meilleur".

Les ensembles de données sont si petits que l'optimiseur n'a pas passé beaucoup de temps à rechercher des alternatives. La première forme de la requête se trouve au début pour trouver un plan utilisant une jointure de hachage et une bobine de table. Le coût estimé de ce plan est si bas que l'optimiseur ne prend pas la peine de chercher quoi que ce soit de mieux.

La deuxième forme de la requête consiste à rechercher un plan en utilisant uniquement des jointures externes de boucles imbriquées au début du processus de recherche, et encore une fois, l'optimiseur décide que le plan est suffisamment bon. Il se trouve que ce plan est estimé moins cher.

Cela dit (comme mentionné dans les commentaires de la question), les deux requêtes ne sont pas sémantiquement identiques. Cela peut ne pas être important pour vous si vous pouvez garantir que les résultats seront toujours les mêmes pour tous les futurs états possibles de votre base de données, mais l'optimiseur ne peut pas faire cette hypothèse. Il ne produit que des plans garantis pour produire les mêmes résultats spécifiés par le SQL, en toutes circonstances.

J'ai réalisé que la syntaxe imbriquée modifie également le comportement de la requête.

La "syntaxe imbriquée" n'est qu'un aspect de toute la spécification de syntaxe de jointure ANSI. Pour activer une spécification logique complète pour des modèles de jointure plus complexes, la spécification autorise les parenthèses (facultatives) et les sous-requêtes de clause FROM.

La requête peut être écrite en utilisant la même syntaxe ANSI en utilisant des parenthèses:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN
(
    dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) ON M.ModelID = A.ModelID;

Ce formulaire montre clairement que l'exigence logique est de laisser la jointure de Autos au résultat de la jointure intérieure Manufacturers à Models. L'omission des parenthèses facultatives donne le formulaire que vous appelez "imbriqué":

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Autos AS A
LEFT JOIN dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
    ON M.ModelID = A.ModelID;

Ce n'est pas une syntaxe différente - il s'agit simplement d'omettre les parenthèses facultatives et de reformater un peu.

Comme Martin l'a mentionné, il est également possible dans ce cas d'exprimer l'exigence logique en utilisant des jointures internes suivies d'une jointure externe droite:

SELECT
    A.*,
    M.*,
    N.* 
FROM dbo.Manufacturers AS N
JOIN dbo.Models AS M
    ON M.ManufacturerID = N.ManufacturerID
RIGHT JOIN dbo.Autos AS A
    ON A.ModelID = M.ModelID;

Les trois formulaires de requête ci-dessus utilisent la même syntaxe de jointure ANSI. Les trois produisent également le même plan d'exécution physique avec l'ensemble de données fourni:

Common execution plan

Comme je l'ai mentionné dans ma réponse à votre question précédente, les requêtes qui expriment exactement la même exigence logique ne produiront pas toujours le même plan d'exécution. La forme de requête logique que vous préférez utiliser est en grande partie une question de style. Il n'y a pas de corrélation entre un style particulier et les "meilleurs" plans de requête en général. Je déconseille généralement de réécrire une requête pour obtenir un plan particulier si la nouvelle requête n'est pas vraiment identique logiquement à l'original.

Le standard SQL autorise également les sous-requêtes de clause FROM, donc une autre façon d'écrire la même spécification de requête est:

SELECT * 
FROM dbo.Autos AS A
LEFT JOIN
(
    SELECT
        N.ManufacturerID,
        ManufacturerName = N.Name,
        M.ModelID,
        ModelName = M.Name
    FROM dbo.Manufacturers AS N
    JOIN dbo.Models AS M
        ON M.ManufacturerID = N.ManufacturerID
) AS R1
    ON R1.ModelID = A.ModelID;

En utilisant la syntaxe traditionnelle, nous devons changer la jointure en `Fabricants en une jointure externe, comme ça ... mais cela change le plan de requête.

Cela change probablement le sens de la requête, auquel cas ce n'est techniquement pas une alternative valide (mais voyez ypercube 's commentaire sur votre question).

Les parenthèses (facultatives) dans la syntaxe de jointure ANSI sont là précisément pour des exigences de jointure plus complexes comme celle-ci, vous ne devriez donc pas avoir peur de les utiliser si nécessaire.

12
Paul White 9