Tengo una consulta que se ejecuta en 800 milisegundos en SQL Server 2012 y tarda unos 170 segundos en SQL Server 2014 . Creo que he reducido esto a una estimación de cardinalidad pobre para el Row Count Spool
operador. He leído un poco sobre los operadores de spool (por ejemplo, aquí y aquí ), pero todavía tengo problemas para entender algunas cosas:
- ¿Por qué esta consulta necesita un
Row Count Spool
operador? No creo que sea necesario para la corrección, entonces, ¿qué optimización específica está tratando de proporcionar? - ¿Por qué SQL Server estima que la unión al
Row Count Spool
operador elimina todas las filas? - ¿Es esto un error en SQL Server 2014? Si es así, presentaré en Connect. Pero me gustaría una comprensión más profunda primero.
Nota: Puedo reescribir la consulta como LEFT JOIN
o agregar índices a las tablas para lograr un rendimiento aceptable tanto en SQL Server 2012 como en SQL Server 2014. Por lo tanto, esta pregunta trata más sobre cómo entender esta consulta específica y planificar en profundidad y menos sobre cómo formular la consulta de manera diferente.
La consulta lenta
Vea este Pastebin para un script de prueba completo. Aquí está la consulta de prueba específica que estoy viendo:
-- 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: el plan de consulta estimado
SQL Server considera que el Left Anti Semi Join
al Row Count Spool
filtrará los 10.000 filas por debajo de 1 fila. Por este motivo, selecciona a LOOP JOIN
para la unión posterior a #existingCustomers
.
SQL Server 2014: el plan de consulta real
Como era de esperar (¡todos menos SQL Server!), Row Count Spool
No eliminó ninguna fila. Por lo tanto, estamos realizando un ciclo de 10,000 veces cuando SQL Server esperaba que se repitiera solo una vez.
SQL Server 2012: el plan de consulta estimado
Cuando se usa SQL Server 2012 (o OPTION (QUERYTRACEON 9481)
en SQL Server 2014), Row Count Spool
no reduce el número estimado de filas y se elige una combinación hash, lo que resulta en un plan mucho mejor.
La REUNIÓN DE LA IZQUIERDA
Como referencia, aquí hay una forma en la que puedo volver a escribir la consulta para lograr un buen rendimiento en todos los SQL Server 2012, 2014 y 2016. Sin embargo, todavía estoy interesado en el comportamiento específico de la consulta anterior y si es un error en el nuevo estimador de cardinalidad de 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