Mam zapytanie, które działa w 800 milisekundach w SQL Server 2012 i zajmuje około 170 sekund w SQL Server 2014 . Myślę, że zawęziłem to do złej oceny liczności dla Row Count Spool
operatora. Przeczytałem trochę o operatorach buforowania (np. Tutaj i tutaj ), ale nadal mam problem ze zrozumieniem kilku rzeczy:
- Dlaczego to zapytanie wymaga
Row Count Spool
operatora? Nie uważam, że jest to konieczne do poprawności, więc jaką konkretną optymalizację stara się zapewnić? - Dlaczego SQL Server ocenia, że połączenie z
Row Count Spool
operatorem usuwa wszystkie wiersze? - Czy to błąd w SQL Server 2014? Jeśli tak, złożę zgłoszenie w Connect. Ale najpierw chciałbym głębszego zrozumienia.
Uwaga: mogę ponownie napisać zapytanie jako a LEFT JOIN
lub dodać indeksy do tabel, aby osiągnąć akceptowalną wydajność zarówno w SQL Server 2012, jak i SQL Server 2014. Więc to pytanie dotyczy bardziej zrozumienia tego konkretnego zapytania i szczegółowego planowania, a mniej o jak inaczej sformułować zapytanie.
Wolne zapytanie
Zobacz ten Pastebin, aby uzyskać pełny skrypt testowy. Oto konkretne zapytanie testowe, na które patrzę:
-- 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: szacowany plan zapytań
SQL Server uważa, że Left Anti Semi Join
do Row Count Spool
filtruje 10 000 wierszy w dół do 1 wiersza. Z tego powodu wybiera LOOP JOIN
dla kolejnego przyłączenia do #existingCustomers
.
SQL Server 2014: rzeczywisty plan zapytań
Zgodnie z oczekiwaniami (przez wszystkich oprócz SQL Server!) Row Count Spool
Nie usunięto żadnych wierszy. Zapętlamy więc 10 000 razy, gdy SQL Server spodziewa się zapętlić tylko raz.
SQL Server 2012: szacowany plan zapytań
Podczas korzystania z SQL Server 2012 (lub OPTION (QUERYTRACEON 9481)
SQL Server 2014), Row Count Spool
nie zmniejsza szacowanej liczby wierszy i wybierane jest łączenie mieszające, co daje znacznie lepszy plan.
Ponowne zapisanie LEWEGO DOŁĄCZENIA
Dla odniesienia, oto sposób, w jaki mogę ponownie napisać zapytanie, aby osiągnąć dobrą wydajność we wszystkich SQL Server 2012, 2014 i 2016. Jednak nadal interesuje mnie określone zachowanie powyższego zapytania i to, czy jest błędem w nowym 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
źródło