Database
 sql >> Base de données >  >> RDS >> Database

Objectifs de ligne, partie 3 :Anti-jointures

Cet article fait partie d'une série d'articles sur les objectifs de ligne. Vous pouvez trouver les autres parties ici :

  • Partie 1 :Définir et identifier des objectifs de ligne
  • Partie 2 :semi-jointures

Cette partie explique quand et pourquoi l'optimiseur introduit un objectif de ligne pour une anti-jointure.

Présentation

Une anti-jointure est également connue sous le nom d'anti-semi-jointure. Il renvoie chaque ligne de l'entrée de jointure A pour laquelle aucune correspondance se trouve sur l'entrée B.

Pour une anti jointure :

  • L'optimiseur peut ajouter un objectif de ligne intérieure à une application (jointure de boucles imbriquées corrélées) anti-jointure uniquement .
  • Un objectif de ligne n'est pas ajouté pour les boucles imbriquées non corrélées, l'anti-jointure, l'anti-jointure par hachage ou l'anti-jointure par fusion.
  • Comme toujours, tout objectif de ligne est seulement ajouté s'il est inférieur à l'estimation sans objectif de ligne appliqué.
  • Côté interne redondant TOP clauses et DISTINCT/GROUP BY les opérations peuvent être simplifiées.

En développant le premier point ci-dessus, la principale différence entre les objectifs d'application de semi-jointure et d'application d'anti-jointure est :

  • Une application de semi-jointure comporte toujours un objectif de ligne (tant qu'il est inférieur à l'estimation sans l'objectif).
  • Une application anti-jointure peut comporter un objectif de ligne , mais uniquement si une anti-jointure logique est transformée en application lors de l'optimisation basée sur les coûts .

Je m'excuse que ces règles ne soient pas plus simples, mais je ne les ai pas faites. Espérons que quelques discussions et exemples rendront tout cela plus clair.

Pas d'objectif anti-jointure par défaut

L'optimiseur suppose que les utilisateurs écrivent une semi-jointure (indirectement, par exemple en utilisant EXISTS ) dans l'espoir que la ligne recherchée sera trouvée . Un objectif de ligne d'application de semi-jointure est défini par l'optimiseur pour aider à trouver rapidement la ligne correspondante attendue.

Pour l'anti-jointure (exprimé par exemple en utilisant NOT EXISTS ) l'hypothèse de l'optimiseur est qu'une ligne correspondante ne sera pas trouvée . Un objectif d'application anti-jointure ligne n'est pas défini par l'optimiseur, car il s'attend à devoir vérifier toutes les lignes pour confirmer qu'il n'y a pas de correspondance.

S'il s'avère qu'il y a une ligne correspondante, l'application anti-jointure peut prendre plus de temps pour localiser cette ligne que si un objectif de ligne avait été utilisé. Néanmoins, l'anti-jointure terminera toujours sa recherche dès que la correspondance (inattendue) sera rencontrée.

Appliquer les conditions d'objectif anti-jointure

SQL Server ne nous fournit pas un moyen d'écrire directement une anti-jointure, nous devons donc utiliser des solutions de contournement comme NOT EXISTS , NOT IN/ANY/SOME , ou EXCEPT . Chacune de ces formes aboutit à une représentation de sous-requête dans l'arborescence analysée au début de la compilation de la requête. Cette sous-requête est toujours déroulée dans une application, puis transformée en une anti-jointure logique si possible (les détails sont les mêmes que pour la semi-jointure discutée dans la partie 2). Tout cela se produit avant même qu'un plan trivial ne soit envisagé.

Pour qu'une anti-jointure obtienne un objectif de ligne, elle doit entrer optimisation basée sur les coûts en tant qu'anti-jointure logique (ce qui signifie que la transformation d'une application ci-dessus doit avoir réussi). Ensuite, l'optimiseur basé sur les coûts doit choisir d'implémenter l'anti-jointure logique en tant qu'application . Pour que cela se produise, l'optimiseur doit d'abord choisir d'explorer l'option d'application ; alors il doit sélectionner cela comme l'option la moins chère (pour cette partie du plan).

Un objectif de ligne anti-jointure est défini par l'une des règles d'optimisation basées sur les coûts qui peuvent transformer une jointure en application. Une anti jointure qui entre optimisation basée sur les coûts comme application (parce que la transformation en anti-jointure logique a échoué) ne sera pas avoir un objectif de ligne appliqué.

L'optimiseur basé sur les coûts n'explorera et ne sélectionnera l'option de jointure pour appliquer que s'il existe un moyen efficace de localiser les lignes latérales intérieures correspondantes (par exemple, à l'aide d'un index). L'optimiseur n'explorera pas l'option s'il manque quelque chose d'utile à la sous-arborescence du côté intérieur de la jointure pour que le prédicat d'application "s'accroche à". Il peut s'agir d'un index, d'un index temporaire (via un spool d'index hâtif) ou d'une autre clé logique. L'ajout de l'objectif de ligne aide l'optimiseur à évaluer le coût de l'option de jointure pour appliquer étant donné qu'un maximum d'une ligne doit être localisé.

Notez qu'une application anti-jointure peut apparaître dans un plan d'exécution sans objectif de ligne. Cela se produit lorsque la transformation initiale de l'application à la jointure échoue, ce qui est relativement courant. Lorsque cela se produit, l'anti-jointure commence sa vie dans l'optimiseur basé sur les coûts en tant qu'application, et donc jamais un objectif de ligne n'est ajouté par l'une des règles de jointure à appliquer.

Bien sûr, un objectif de ligne peut également être introduit sur le côté intérieur de cette application via un mécanisme différent (non associé à l'application), par exemple par un opérateur Top séparé.

Pour résumer :

  • Une anti-jointure ne peut gagner un objectif de ligne que pendant l'optimisation basée sur les coûts (CBO).
  • Les règles qui traduisent une anti-jointure en un objectif d'application ajoutent un objectif de ligne.
  • L'anti-jointure doit saisir CBO en tant que jointure, et non en tant qu'application.
  • Pour entrer CBO en tant que jointure, les phases précédentes doivent pouvoir réécrire la sous-requête en tant que jointure (via une étape d'application).
  • CBO n'explore la jointure pour appliquer la transformation que dans les cas prometteurs.

Exemple

Il est un peu plus délicat de démontrer tout cela pour appliquer l'anti-jointure que ce n'était le cas pour appliquer la semi-jointure. Les raisons en seront couvertes dans la partie 4.

En attendant, voici un exemple AdventureWorks montrant comment un objectif d'application anti-jointure avec ligne se produit, en utilisant les mêmes indicateurs de trace non documentés que pour la semi-jointure. L'indicateur de trace 8608 est ajouté pour afficher la structure de mémo initiale au début de l'optimisation basée sur les coûts.

SELECT P.ProductID 
FROM Production.Product AS P
WHERE 
    NOT EXISTS 
    (
        SELECT 1
        FROM Production.TransactionHistoryArchive AS THA 
        WHERE THA.ProductID = P.ProductID
 
        UNION ALL
 
        SELECT 1
        FROM Production.TransactionHistory AS TH 
        WHERE TH.ProductID = P.ProductID
    )
OPTION (QUERYTRACEON 3604, QUERYTRACEON 8607, QUERYTRACEON 8608, QUERYTRACEON 8612, QUERYTRACEON 8621);

La sous-requête "existe" est d'abord transformée en "applique" :