J'ai une table avec quelques dizaines de lignes. La configuration simplifiée suit
CREATE TABLE #data ([Id] int, [Status] int);
INSERT INTO #data
VALUES (100, 1), (101, 2), (102, 3), (103, 2);
Et j'ai une requête qui joint cette table à un ensemble de lignes construites de valeur de table (faites de variables et de constantes), comme
DECLARE @id1 int = 101, @id2 int = 105;
SELECT
COALESCE(p.[Code], 'X') AS [Code],
COALESCE(d.[Status], 0) AS [Status]
FROM (VALUES
(@id1, 'A'),
(@id2, 'B')
) p([Id], [Code])
FULL JOIN #data d ON d.[Id] = p.[Id];
Le plan d'exécution de la requête montre que la décision de l'optimiseur est d'utiliser la FULL LOOP JOIN
stratégie, ce qui semble approprié, car les deux entrées ont très peu de lignes. Une chose que j'ai remarquée (et je ne suis pas d'accord), cependant, c'est que les lignes TVC sont mises en file d'attente (voir la zone du plan d'exécution dans l'encadré rouge).
Pourquoi l'optimiseur introduit le spool ici, quelle est la raison de le faire? Il n'y a rien de complexe au-delà de la bobine. On dirait que ce n'est pas nécessaire. Comment s'en débarrasser dans ce cas, quelles sont les voies possibles?
Le plan ci-dessus a été obtenu le
Microsoft SQL Server 2014 (SP2-CU11) (KB4077063) - 12.0.5579.0 (X64)
Réponses:
La chose au-delà du spool n'est pas une simple référence de table, qui pourrait simplement être dupliquée lorsque l' alternative gauche join / anti semi join est générée.
Cela peut ressembler un peu à une table (Constant Scan) mais pour l'optimiseur * c'est une
UNION ALL
des lignes séparées dans laVALUES
clause.La complexité supplémentaire est suffisante pour que l'optimiseur choisisse de spouler et de relire les lignes source, et de ne pas remplacer le spouleur par un simple "get table" plus tard. Par exemple, la transformation initiale de la jointure complète ressemble à ceci:
Remarquez les bobines supplémentaires introduites par la transformation générale. Les bobines situées au-dessus d'une table simple sont nettoyées ultérieurement par la règle
SpoolGetToGet
.Si l'optimiseur avait une
SpoolConstGetToConstGet
règle correspondante , cela pourrait fonctionner comme vous le souhaitez, en principe.Utilisez une vraie table (temporaire ou variable), ou écrivez manuellement la transformation à partir de la jointure complète, par exemple:
Planifier une réécriture manuelle:
Cela a un coût estimé à 0,0067201 unités, contre 0,0203412 unités pour l'original.
* Il peut être observé comme un
LogOp_UnionAll
dans l' arbre converti (TF 8605). Dans l' arborescence d'entrée (TF 8606), il s'agit d'unLogOp_ConstTableGet
. L' arbre converti montre l'arborescence des éléments d'expression de l'optimiseur après l'analyse, la normalisation, l'algèbre, la liaison et certains autres travaux préparatoires. L' arbre d'entrée montre les éléments après la conversion en forme normale de négation (conversion NNF), l'effondrement des constantes d'exécution et quelques autres bits et bobs. La conversion NNF inclut la logique pour réduire les unions logiques et les récupérations de table communes, entre autres.la source
La bobine de table crée simplement une table à partir des deux ensembles de tuples présents dans la
VALUES
clause.Vous pouvez éliminer le spool en insérant d'abord ces valeurs dans une table temporaire, comme ceci:
En regardant le plan d'exécution de votre requête, nous voyons que la liste de sortie contient deux colonnes qui utilisent le
Union
préfixe; c'est un indice que la bobine crée une table à partir d'une source syndiquée:le
FULL OUTER JOIN
requiert que SQL Server accède aux valeursp
deux fois, une fois pour chaque "côté" de la jointure. La création d'un spool permet à la jointure de boucles internes résultante d'accéder aux données spoule.Fait intéressant, si vous remplacez le
FULL OUTER JOIN
par unLEFT JOIN
et unRIGHT JOIN
etUNION
les résultats ensemble, SQL Server n'utilise pas de spoule.Remarque, je ne suggère pas d'utiliser la
UNION
requête ci-dessus; pour de plus grands ensembles d'entrée, il peut ne pas être plus efficace que le simple queFULL OUTER JOIN
vous avez déjà.la source