Puzle Otimizador de consultas do SQL Server 2008 R2
Temos duas tabelas, ambas contendo 9 milhões de linhas. 70.000 linhas são diferentes, as outras são iguais.
Isso é rápido, 13 segundos,
select * from bigtable1
except select * from similar_bigtable2
Isso classifica a saída e também é rápido, 13 segundos também,
select * into #q from bigtable1
except select * from similar_bigtable2
select * from #q order by sort_column
Embora isso seja muito lento:
;with q as (
select * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
E mesmo um "truque" que às vezes uso para sugerir ao SQL Server que ele precisa pré-calcular uma certa parte da consulta antes de prosseguir, não funciona e resulta em consulta lenta também:
;with q as (
select top 100 percent * from bigtable1
except select * from similar_bigtable2
)
select * from q order by sort_column
Observando os planos de consulta, não é difícil encontrar o motivo:
O SQL Server coloca dois tipos de 9 milhões de linhas antes do hashmatch, enquanto eu preferiria ter adicionado apenas um tipo de 70.000 linhas após o hashmatch.
Então a pergunta: como instruir o otimizador de consulta para fazer isso?
fonte
EXCEPT
(por exemploOUTER JOIN
)? Sei que a sintaxe é menos conveniente, mas você pode jogar com dicas de índice / junção melhor lá (ou talvez não seja necessário). A alternativa que você está usando agora (primeiro em uma tabela #temp) é uma solução alternativa de último recurso, mas em alguns casos é a única maneira de forçar o otimizador a separar completamente duas partes de uma consulta da maneira que desejar.Respostas:
A principal diferença entre esses dois planos de consulta está na diferença de Hash Match e Merge Join. A correspondência de hash é mais eficiente e, como você pode ver, a consulta é executada mais rapidamente na opção 1 (não usando o CTE).
O CTE é uma ótima ferramenta, mas parece não ser eficiente em dois casos, Predicados Complexos ou Chave Pai / Filho Não Exclusiva. No seu caso, não há uma chave exclusiva e o SQL Server precisa classificar os conjuntos de dados primeiro para poder atender aos seus requisitos. Dê uma olhada no link abaixo, que informa mais sobre esse assunto: http://blogs.msdn.com/b/sqlcat/archive/2011/04/28/optimize-recursive-cte-query.aspx
Portanto, parece que você precisa aceitar sua lentidão ou reescrever a lógica com o loop WHILE, que pode ser mais eficiente.
fonte
Tente isso, melhor?
fonte
Essa não é uma solução ideal, mas se você não conseguir estruturar o tsql para gerar um plano eficiente, poderá definir um guia de plano para forçar o plano que deseja. Fazer isso significaria que, se um plano mais eficiente se tornar disponível, o SQL não o considerará, mas é uma opção.
fonte