У мене є запит, який працює в SQL Server 2012 за 800 мілісекунд і займає близько 170 секунд у SQL Server 2014 . Я думаю, що я звузив це до поганої оцінки кардинальності для Row Count Spool
оператора. Я читав трохи про операторів котушки (наприклад, тут і тут ), але все ще відчуваю проблеми з розумінням кількох речей:
- Чому для цього запиту потрібен
Row Count Spool
оператор? Я не думаю, що це потрібно для коректності, тож яку конкретну оптимізацію намагається надати? - Чому SQL Server оцінює, що приєднання до
Row Count Spool
оператора видаляє всі рядки? - Це помилка в SQL Server 2014? Якщо так, я подаю файл у Connect. Але я хотів би спочатку глибшого розуміння.
Примітка. Я можу перезаписати запит у вигляді LEFT JOIN
або додати індекси до таблиць, щоб досягти прийнятної продуктивності як у SQL Server 2012, так і в SQL Server 2014. Отже, це питання стосується більш глибокого розуміння цього конкретного запиту та плану, а менше про як по-різному фразувати запит.
Повільний запит
Повний скрипт тесту див. На пастебіні . Ось конкретний тестовий запит, на який я дивлюсь:
-- 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: орієнтовний план запитів
SQL Server вважає, що " Left Anti Semi Join
до Row Count Spool
волі" відфільтрує 10000 рядків до 1 ряду. З цієї причини він вибирає LOOP JOIN
для наступного приєднання до #existingCustomers
.
SQL Server 2014: фактичний план запитів
Як і очікувалося (всі, окрім SQL Server!), Row Count Spool
Рядки не видаляли. Таким чином, ми кружляємо в 10000 разів, коли SQL Server очікував циклу лише один раз.
SQL Server 2012: орієнтовний план запитів
Під час використання SQL Server 2012 (або OPTION (QUERYTRACEON 9481)
в SQL Server 2014) Row Count Spool
не зменшується приблизна кількість рядків і вибирається хеш-з'єднання, що призводить до набагато кращого плану.
ЛИВО ПРИЄДНАЙТЕСЬ перепишіть
Для довідки, ось такий спосіб я можу переписати запит, щоб досягти хорошої продуктивності у всіх SQL Server 2012, 2014 та 2016 роках. Однак мене все ще цікавить конкретна поведінка запиту вище та чи це - помилка в новому оцінці кардинальності 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