Ho una query che funziona in 800 millisecondi in SQL Server 2012 e impiega circa 170 secondi in SQL Server 2014 . Penso di averlo ridotto a una stima di cardinalità scadente per l' Row Count Spool
operatore. Ho letto un po 'di operatori di spool (es. Qui e qui ), ma ho ancora problemi a capire alcune cose:
- Perché questa query richiede un
Row Count Spool
operatore? Non credo sia necessario per la correttezza, quindi quale ottimizzazione specifica sta cercando di fornire? - Perché SQL Server stima che il join
Row Count Spool
all'operatore rimuova tutte le righe? - È un bug in SQL Server 2014? In tal caso, invierò il file Connect. Ma prima vorrei una comprensione più profonda.
Nota: posso riscrivere la query come LEFT JOIN
o aggiungere indici alle tabelle al fine di ottenere prestazioni accettabili sia in SQL Server 2012 che in SQL Server 2014. Quindi questa domanda riguarda più la comprensione di questa specifica query e questo piano in modo approfondito e meno come pronunciare la query in modo diverso.
La query lenta
Vedi questo Pastebin per uno script di test completo. Ecco la query di test specifica che sto guardando:
-- 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: il piano di query stimato
SQL Server ritiene che il Left Anti Semi Join
to Row Count Spool
filtra le 10.000 righe fino a 1 riga. Per questo motivo, seleziona a LOOP JOIN
per il successivo join a #existingCustomers
.
SQL Server 2014: il piano di query effettivo
Come previsto (da tutti tranne SQL Server!), Row Count Spool
Non è stata rimossa alcuna riga. Quindi eseguiamo il looping 10.000 volte quando SQL Server prevede di eseguire il loop solo una volta.
SQL Server 2012: il piano di query stimato
Quando si utilizza SQL Server 2012 (o OPTION (QUERYTRACEON 9481)
in SQL Server 2014), Row Count Spool
non riduce il numero stimato di righe e viene scelto un join hash, risultando in un piano molto migliore.
La LEFT JOIN riscrive
Per riferimento, ecco un modo in cui posso riscrivere la query per ottenere buone prestazioni in tutti i SQL Server 2012, 2014 e 2016. Tuttavia, sono ancora interessato al comportamento specifico della query sopra e se è un bug nel nuovo stimatore della cardinalità di 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