Ich habe eine Abfrage, die in SQL Server 2012 in 800 Millisekunden ausgeführt wird und in SQL Server 2014 etwa 170 Sekunden dauert . Ich denke, ich habe dies auf eine schlechte Kardinalitätsschätzung für den Row Count Spool
Bediener eingegrenzt. Ich habe ein wenig über Spool-Operatoren gelesen (z. B. hier und hier ), habe aber immer noch Probleme, einige Dinge zu verstehen:
- Warum benötigt diese Abfrage einen
Row Count Spool
Operator? Ich denke nicht, dass es für die Korrektheit notwendig ist. Welche spezifische Optimierung versucht es also bereitzustellen? - Warum schätzt SQL Server, dass der Join zum
Row Count Spool
Operator alle Zeilen entfernt? - Ist dies ein Fehler in SQL Server 2014? Wenn ja, werde ich in Connect einreichen. Aber ich möchte zuerst ein tieferes Verständnis.
Hinweis: Ich kann die Abfrage als neu schreiben LEFT JOIN
oder den Tabellen Indizes hinzufügen, um eine akzeptable Leistung sowohl in SQL Server 2012 als auch in SQL Server 2014 zu erzielen. Bei dieser Frage geht es also mehr um das Verständnis dieser spezifischen Abfrage und den Plan und weniger um das Planen wie man die Abfrage anders formuliert.
Die langsame Abfrage
In diesem Pastebin finden Sie ein vollständiges Testskript . Hier ist die spezifische Testabfrage, die ich betrachte:
-- 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: Der geschätzte Abfrageplan
SQL Server glaubt , dass die Left Anti Semi Join
auf die Row Count Spool
werden die 10.000 Zeilen bis zu 1 Zeile filtern. Aus diesem Grund wird ein LOOP JOIN
für den nachfolgenden Join ausgewählt #existingCustomers
.
SQL Server 2014: Der eigentliche Abfrageplan
Wie erwartet (von allen außer SQL Server!) Row Count Spool
Wurden keine Zeilen entfernt. Wir führen also 10.000 Schleifen durch, wenn SQL Server nur eine Schleife erwartet.
SQL Server 2012: Der geschätzte Abfrageplan
Bei Verwendung von SQL Server 2012 (oder OPTION (QUERYTRACEON 9481)
in SQL Server 2014) wird die Row Count Spool
geschätzte Anzahl der Zeilen nicht reduziert, und es wird ein Hash-Join ausgewählt, was zu einem weitaus besseren Plan führt.
Der LEFT JOIN schreibt neu
Als Referenz ist hier eine Möglichkeit, die Abfrage neu zu schreiben, um eine gute Leistung in allen SQL Server 2012, 2014 und 2016 zu erzielen. Ich bin jedoch immer noch an dem spezifischen Verhalten der obigen Abfrage interessiert und daran, ob dies der Fall ist ist ein Fehler im neuen SQL Server 2014 Cardinality Estimator.
-- 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