J'ai une requête qui s'exécute en 800 millisecondes dans SQL Server 2012 et prend environ 170 secondes dans SQL Server 2014 . Je pense que j'ai réduit cela à une mauvaise estimation de cardinalité pour l' Row Count Spool
opérateur. J'ai lu un peu sur les opérateurs de spoule (par exemple, ici et ici ), mais j'ai toujours du mal à comprendre certaines choses:
- Pourquoi cette requête a-t-elle besoin d'un
Row Count Spool
opérateur? Je ne pense pas que ce soit nécessaire pour l'exactitude, alors quelle optimisation spécifique essaie-t-elle de fournir? - Pourquoi SQL Server estime-t-il que la jointure à l'
Row Count Spool
opérateur supprime toutes les lignes? - Est-ce un bogue dans SQL Server 2014? Si oui, je déposerai dans Connect. Mais j'aimerais d'abord une compréhension plus profonde.
Remarque: je peux réécrire la requête en tant que LEFT JOIN
ou ajouter des index aux tables afin d'obtenir des performances acceptables à la fois dans SQL Server 2012 et SQL Server 2014. Cette question concerne donc plus la compréhension de cette requête spécifique et la planification en profondeur et moins comment formuler la requête différemment.
La requête lente
Voir ce Pastebin pour un script de test complet. Voici la requête de test spécifique que je regarde:
-- Prune any existing customers from the set of potential new customers
-- This query is much slower than expected in SQL Server 2014
SELECT *
FROM #potentialNewCustomers -- 10K rows
WHERE cust_nbr NOT IN (
SELECT cust_nbr
FROM #existingCustomers -- 1MM rows
)
SQL Server 2014: le plan de requête estimé
SQL Server est d' avis que le Left Anti Semi Join
au Row Count Spool
filtrera les 10.000 lignes jusqu'à 1 rang. Pour cette raison, il sélectionne a LOOP JOIN
pour la jointure suivante #existingCustomers
.
SQL Server 2014: le plan de requête réel
Comme prévu (par tout le monde sauf SQL Server!), Le Row Count Spool
n'a supprimé aucune ligne. Nous bouclons donc 10 000 fois lorsque SQL Server ne devrait boucler qu'une seule fois.
SQL Server 2012: le plan de requête estimé
Lorsque vous utilisez SQL Server 2012 (ou OPTION (QUERYTRACEON 9481)
dans SQL Server 2014), le Row Count Spool
ne réduit pas le nombre estimé de lignes et une jointure de hachage est choisie, ce qui donne un bien meilleur plan.
La réécriture LEFT JOIN
Pour référence, voici un moyen de réécrire la requête afin d'obtenir de bonnes performances dans tous les SQL Server 2012, 2014 et 2016. Cependant, je suis toujours intéressé par le comportement spécifique de la requête ci-dessus et si elle est un bogue dans le nouvel estimateur de cardinalité SQL Server 2014.
-- Re-writing with LEFT JOIN yields much better performance in 2012/2014/2016
SELECT n.*
FROM #potentialNewCustomers n
LEFT JOIN (SELECT 1 AS test, cust_nbr FROM #existingCustomers) c
ON c.cust_nbr = n.cust_nbr
WHERE c.test IS NULL