Eu tenho uma instrução delete que está usando um plano ruim quando executado dentro de um procedimento armazenado, mas está escolhendo um plano muito melhor quando executado ad-hoc.
Reconstruí todos os índices das tabelas usadas pela consulta e eliminei todos os caches. O otimizador ainda escolhe o plano errado para o procedimento armazenado.
Gostaria de saber por que o otimizador está usando um plano de execução diferente para o procedimento armazenado versus o SQL ad-hoc.
Suspeitos do costume:
constantes em ad hoc, parâmetros em código.
O otimizador pode então escolher o melhor plano para as constantes.
Alterar as constantes = alterar o plano. Um plano parametrizado é reutilizável.
incompatibilidade de tipos de dados no código.
introduzirá conversões implícitas devido à precedência de tipo de dados.
por exemplo, coluna varchar comparada ao parâmetro nvarchar.
detecção de parâmetros.
use mascaramento de parâmetro ou OTIMIZE PARA DESCONHECIDO
Para testar: execute stored proc, execute
sp_updatestats
, execute novamente. Isso invalidará os planos em cache, o que é melhor do que limpar todo o cache do plano.Você pode desativar o sniffing de várias maneiras. Os 3 principais são
RECOMPILE
. Isso é bobagem na minha opinião.OPTIMIZE FOR UNKNOWN
O mascaramento de parâmetros se parece com isto:
O mascaramento e a
OPTIMIZE
dica têm o mesmo efeito (talvez por motivos diferentes). Ou seja, o otimizador tem que usar estatísticas e distribuição de dados paraavaliar os parâmetros por seus próprios méritos ?, em vez do que eram na última chamada. O otimizador pode recompilar ou não. O SQL Server 2005 adicionou recompilação em nível de instrução para que houvesse menos impacto.Agora, por que um plano com parâmetros "sniffed" é "pegajoso" em comparação com parâmetros mascarados/"desconhecidos", não tenho certeza.
Eu usei o mascaramento de parâmetros desde o SQL Server 2000 para todos, exceto o código mais simples. Observei que isso pode acontecer com códigos mais complexos. E no meu antigo emprego, tenho alguns processos de relatório que eu poderia alterar os padrões de parâmetro do plano. Acho que a abordagem de "culto à carga" foi mais fácil do que uma chamada de suporte.
O mascaramento de parâmetros e
OPTIMISE FOR UNKNOWN
tem o mesmo efeito, tanto quanto eu posso dizer.A dica é mais limpa que a máscara, mas foi adicionada com o SQL Server 2008.
A detecção de parâmetros ocorre em tempo de compilação.
WITH RECOMPILE
gera um novo plano a cada execução. Isso significa que uma má escolha de padrões influenciará o plano. No meu último trabalho, pude demonstrar isso facilmente com algum código de relatório: a alteração dos padrões dos parâmetros alterou o plano independentemente dos parâmetros fornecidos.Este artigo do MS Connect é interessante: Uso de índice abaixo do ideal no procedimento armazenado (mencionado em uma das respostas do SO abaixo)
Bob Beauchemin menciona isso também
Problemas pendentes
O sniffing ainda se aplica a
WITH RECOMPILE
? Ou seja, se o otimizador sabe descartar o plano ele visa a reutilização?Por que os planos cheirados são "pegajosos"?
Links do SO:
https://stackoverflow.com/q/6919166/27535
https://stackoverflow.com/q/5236631/27535
https://stackoverflow.com/q/1392307/27535
https://stackoverflow.com/q/2905440/27535
https://stackoverflow.com/q/467513/27535
https://stackoverflow.com/q/272726/27535
Remus Rusanu (equipe MS SQL) e eu brigamos aqui no SO
Whitepaper sobre MSDN em conjunto com minha análise SO: https://stackoverflow.com/questions/414336/why-does-the-sqlserver-optimizer-get-so-confused-with-parameters/414368#414368
Não se esqueça de que as configurações ANSI que você configurou para o plano de conexão têm um papel na seleção do plano de execução. Quando o aplicativo chama o procedimento armazenado, ele provavelmente tem configurações ANSI diferentes da sua conexão SSMS.