Eu tenho uma consulta que é executada em 800 milissegundos no SQL Server 2012 e leva cerca de 170 segundos no SQL Server 2014 . Eu acho que reduzi isso a uma estimativa de cardinalidade ruim para o Row Count Spool
operador. Eu li um pouco sobre operadores de spool (por exemplo, aqui e aqui ), mas ainda estou tendo problemas para entender algumas coisas:
- Por que essa consulta precisa de um
Row Count Spool
operador? Eu não acho que seja necessário para correção, então que otimização específica ela está tentando fornecer? - Por que o SQL Server estima que a associação ao
Row Count Spool
operador remove todas as linhas? - Isso é um bug no SQL Server 2014? Nesse caso, vou arquivar no Connect. Mas eu gostaria de ter um entendimento mais profundo primeiro.
Nota: Posso reescrever a consulta como um LEFT JOIN
ou adicionar índices às tabelas para obter um desempenho aceitável no SQL Server 2012 e no SQL Server 2014. Portanto, esta pergunta é mais sobre como entender essa consulta específica e planejar detalhadamente e menos sobre como formular a consulta de maneira diferente.
A consulta lenta
Consulte este Pastebin para obter um script de teste completo. Aqui está a consulta de teste específica que estou procurando:
-- 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: O plano de consulta estimado
SQL Server acredita que o Left Anti Semi Join
à Row Count Spool
vai filtrar as linhas 10.000 até 1 linha. Por esse motivo, ele seleciona a LOOP JOIN
para a junção subsequente #existingCustomers
.
SQL Server 2014: O plano de consulta real
Como esperado (por todos, exceto pelo SQL Server!), O Row Count Spool
não removeu nenhuma linha. Então, estamos repetindo 10.000 vezes quando o SQL Server espera repetir apenas uma vez.
SQL Server 2012: O plano de consulta estimado
Ao usar o SQL Server 2012 (ou OPTION (QUERYTRACEON 9481)
no SQL Server 2014), Row Count Spool
isso não reduz o número estimado de linhas e uma associação de hash é escolhida, resultando em um plano muito melhor.
A reescrita LEFT JOIN
Para referência, aqui está uma maneira de reescrever a consulta para obter um bom desempenho em todos os SQL Server 2012, 2014 e 2016. No entanto, ainda estou interessado no comportamento específico da consulta acima e se ela é um erro no novo estimador de cardinalidade do 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