У меня есть запрос, который выполняется в 800 миллисекунд в SQL Server 2012 и занимает около 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. Поэтому этот вопрос больше о понимании этого конкретного запроса и подробном планировании, а не о как сформулировать запрос по-другому.
Медленный запрос
Смотрите этот Pastebin для полного сценария тестирования. Вот конкретный тестовый запрос, на который я смотрю:
-- 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
не уменьшается предполагаемое количество строк, и выбирается хеш-соединение, что приводит к гораздо лучшему плану.
LEFT JOIN переписать
Для справки, вот способ, которым я могу переписать запрос, чтобы добиться хорошей производительности во всех 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