Pourquoi cette requête n'utilise-t-elle pas une bobine d'index?


23

Je pose cette question afin de mieux comprendre le comportement de l'optimiseur et de comprendre les limites autour des spools d'index. Supposons que je mette des entiers de 1 à 10000 dans un tas:

CREATE TABLE X_10000 (ID INT NOT NULL);
truncate table X_10000;

INSERT INTO X_10000 WITH (TABLOCK)
SELECT TOP 10000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

Et forcez une boucle imbriquée à se joindre à MAXDOP 1:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);

Il s'agit d'une action plutôt hostile à entreprendre envers SQL Server. Les jointures de boucles imbriquées ne sont souvent pas un bon choix lorsque les deux tables n'ont pas d'index pertinents. Voici le plan:

mauvaise requête

La requête prend 13 secondes sur ma machine avec 100000000 lignes extraites de la bobine de table. Cependant, je ne vois pas pourquoi la requête doit être lente. L'optimiseur de requêtes a la possibilité de créer des index à la volée via des spools d'index . Cette requête semble être un candidat parfait pour une bobine d'index.

La requête suivante renvoie les mêmes résultats que la première, possède une bobine d'index et se termine en moins d'une seconde:

SELECT *
FROM X_10000 a
CROSS APPLY (SELECT TOP (9223372036854775807) b.ID FROM X_10000 b WHERE a.ID = b.ID) ca
OPTION (LOOP JOIN, MAXDOP 1);

solution de contournement 1

Cette requête a également une bobine d'indexation et se termine en moins d'une seconde:

SELECT *
FROM X_10000 a
INNER JOIN X_10000 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (LOOP JOIN, MAXDOP 1);

solution de contournement 2

Pourquoi la requête d'origine n'a-t-elle pas de bobine d'index? Existe-t-il un ensemble d'indications ou d'indicateurs de trace documentés ou non qui lui donneront une file d'attente d'index? J'ai trouvé cette question connexe , mais elle ne répond pas entièrement à ma question et je ne peux pas faire fonctionner l'indicateur de trace mystérieux pour cette requête.

Réponses:


20

Comme vous le savez, la recherche de l'optimiseur n'est pas exhaustive. Il essaie des choses qui ont du sens dans le contexte et qui paient souvent des dividendes sur de vraies requêtes. Forcer une jointure en boucle entre deux tables de segments non indexés à colonne unique n'est pas un tel scénario. Cela dit, voici quelques détails:

SQL Server aime transformer tôt s'applique aux jointures, car il connaît plus d'astuces avec les jointures. Plus tard, il pourra envisager de reconvertir la jointure en demande. La différence entre les deux étant des paramètres corrélés (références externes). S'applique a un sens quand il y a un index approprié sur le côté intérieur. Votre exemple n'a pas d'index, donc l'optimiseur n'est pas convaincu d'explorer la traduction vers une application.

Une jointure simple (non applicable) a le prédicat de jointure sur l'opérateur de jointure au lieu des références externes. L'optimisation du spool pour une non-application est généralement un spool de table paresseux, car il n'y a pas de prédicat du côté interne, uniquement au niveau de la jointure.

L'optimiseur n'envisage pas de créer un index à la volée pour activer une application; la séquence des événements est plutôt l'inverse: transformer pour appliquer car un bon index existe.

Vous pouvez parfois encourager une application plutôt qu'une jointure en utilisant la APPLYsyntaxe dans votre requête. L'indicateur de trace non documenté 9114 peut aider à cela en dissuadant l'optimiseur de traduire une application logique en une jointure initiale. Par exemple:

SELECT * 
FROM dbo.X_1000 AS a
CROSS APPLY (SELECT * FROM dbo.X_1000 AS b WHERE b.ID = a.ID) AS b
OPTION (QUERYTRACEON 9114);

Plan de bobine

Une bobine d'index est privilégiée pour appliquer car la référence externe signifie que la sélection est appliquée sur le côté interne de la jointure. Vous le verrez souvent via SelToIndexOnTheFlymais d'autres chemins existent. Voir mon article The Eager Index Spool and The Optimizer .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.