A consulta é
SELECT SUM(Amount) AS SummaryTotal
FROM PDetail WITH(NOLOCK)
WHERE ClientID = @merchid
AND PostedDate BETWEEN @datebegin AND @dateend
A tabela contém 103.129.000 linhas.
O plano rápido procura pelo ClientId com um predicado residual na data, mas precisa fazer 96 pesquisas para recuperar o Amount
. A <ParameterList>
seção no plano é a seguinte.
<ParameterList>
<ColumnReference Column="@dateend"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@datebegin"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@merchid"
ParameterRuntimeValue="(78155)" />
</ParameterList>
O plano lento pesquisa por data e possui pesquisas para avaliar o predicado residual no ClientId e recuperar a quantia (Estimado 1 versus Real 7.388.383). A <ParameterList>
seção é
<ParameterList>
<ColumnReference Column="@EndDate"
ParameterCompiledValue="'2013-02-01 23:59:00.000'"
ParameterRuntimeValue="'2013-02-01 23:59:00.000'" />
<ColumnReference Column="@BeginDate"
ParameterCompiledValue="'2013-01-01 00:00:00.000'"
ParameterRuntimeValue="'2013-01-01 00:00:00.000'" />
<ColumnReference Column="@ClientID"
ParameterCompiledValue="(78155)"
ParameterRuntimeValue="(78155)" />
</ParameterList>
Neste segundo caso, o nãoParameterCompiledValue
está vazio. O SQL Server detectou com êxito os valores usados na consulta.
O livro "Solução de problemas práticos do SQL Server 2005" tem a dizer sobre o uso de variáveis locais
Usar variáveis locais para impedir o sniffing de parâmetros é um truque bastante comum, mas as dicas OPTION (RECOMPILE)
e OPTION (OPTIMIZE FOR)
... são geralmente mais elegantes e soluções um pouco menos arriscadas
Nota
No SQL Server 2005, a compilação no nível da instrução permite que a compilação de uma instrução individual em um procedimento armazenado seja adiada até pouco antes da primeira execução da consulta. Até então, o valor da variável local seria conhecido. Teoricamente, o SQL Server poderia tirar proveito disso para detectar valores de variáveis locais da mesma maneira que detecta parâmetros. No entanto, como era comum o uso de variáveis locais para impedir o sniffing de parâmetro no SQL Server 7.0 e SQL Server 2000+, o sniffing de variáveis locais não estava habilitado no SQL Server 2005. Ele pode ser habilitado em uma versão futura do SQL Server, o que é uma boa opção. motivo para usar uma das outras opções descritas neste capítulo, se você tiver uma escolha.
De um teste rápido para esse fim, o comportamento descrito acima ainda é o mesmo em 2008 e 2012 e as variáveis não são detectadas para compilação adiada, mas apenas quando uma OPTION RECOMPILE
dica explícita é usada.
DECLARE @N INT = 0
CREATE TABLE #T ( I INT );
/*Reference to #T means this statement is subject to deferred compile*/
SELECT *
FROM master..spt_values
WHERE number = @N
AND EXISTS(SELECT COUNT(*) FROM #T)
SELECT *
FROM master..spt_values
WHERE number = @N
OPTION (RECOMPILE)
DROP TABLE #T
Apesar da compilação adiada, a variável não é detectada e a contagem de linhas estimada é imprecisa
Portanto, suponho que o plano lento esteja relacionado a uma versão parametrizada da consulta.
O ParameterCompiledValue
é igual a ParameterRuntimeValue
para todos os parâmetros, portanto, este não é um sniffing típico de parâmetros (onde o plano foi compilado para um conjunto de valores e depois executado para outro conjunto de valores).
O problema é que o plano que é compilado para os valores corretos dos parâmetros é inadequado.
Você provavelmente está enfrentando o problema com datas ascendentes descritas aqui e aqui . Para uma tabela com 100 milhões de linhas, você precisa inserir (ou modificar) 20 milhões antes que o SQL Server atualize automaticamente as estatísticas para você. Parece que da última vez em que foram atualizadas, zero linhas correspondiam ao período da consulta, mas agora 7 milhões o fazem.
Você pode agendar atualizações de estatísticas mais frequentes, considerar sinalizadores de rastreio 2389 - 90
ou usá- OPTIMIZE FOR UKNOWN
lo, para que apenas recorra a suposições, em vez de poder usar as estatísticas atualmente enganosas na datetime
coluna.
Isso pode não ser necessário na próxima versão do SQL Server (após 2012). Um item do Connect relacionado contém a resposta intrigante
Publicado pela Microsoft em 28/08/2012 às 13:35
Fizemos um aprimoramento na estimativa de cardinalidade para o próximo grande lançamento que basicamente corrige isso. Fique atento aos detalhes quando as nossas pré-visualizações forem publicadas. Eric
Esta melhoria de 2014 é analisada por Benjamin Nevarez no final do artigo:
Uma primeira olhada no novo estimador de cardinalidade do SQL Server .
Parece que o novo estimador de cardinalidade voltará e usará a densidade média nesse caso, em vez de fornecer a estimativa de 1 linha.
Alguns detalhes adicionais sobre o estimador de cardinalidade de 2014 e o principal problema crescente aqui:
Nova funcionalidade no SQL Server 2014 - Parte 2 - Nova estimativa de cardinalidade
Eu tive exatamente o mesmo problema em que um procedimento armazenado ficou lento
OPTIMIZE FOR UNKNOWN
e asRECOMPILE
dicas de consulta resolveram a lentidão e aceleraram o tempo de execução. No entanto, os dois métodos a seguir não afetaram a lentidão do procedimento armazenado: (i) Limpando o cache (ii) usando WITH RECOMPILE. Assim, como você disse, não era realmente um farejador de parâmetros.Os sinalizadores de rastreamento 2389 e 2390 também não ajudaram. Apenas atualizar as estatísticas (
EXEC sp_updatestats
) fez isso por mim.fonte