O código cria um plano diferente quando executado ad-hoc vs. em um procedimento armazenado

9

Eu tenho uma instrução de exclusão que está usando um plano incorreto quando executado dentro de um procedimento armazenado, mas está escolhendo um plano muito melhor quando executado ad-hoc.

Eu 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.

ATUALIZAÇÃO: Eu acho que deve ter sido os parâmetros, afinal - quando eu executei o código ad-hoc com a variável codificada, posso obter o plano "ruim" com o valor certo (é uma data, valores com um ano de idade parecem gerar o plano "bom"). Agora, tente forçar o plano "bom" no proc usando uma dica de consulta.

SOLUÇÃO: Acabei obtendo o plano desejado usando a dica OPTIMIZE FOR UNKNOWN.

msgisme
fonte
Provavelmente é o cheiro dos parâmetros . A consulta faz referência a uma variável em sua WHEREcláusula?
Nick Chammas
4
Você pode adicionar código, por favor
gbn 4/11
Publique também os planos em algum lugar, por favor. Como é, será muito difícil dizer exatamente por que os planos são diferentes.
Aaron Bertrand
OK, mais informações: não posso postar os planos e códigos até ofuscá-los um pouco. Vou tentar levantá-los em alguns. O plano para o procedimento armazenado (incorreto) faz uma verificação de índice em cluster de uma tabela grande (a coisa toda, todas as partições). Em seguida, ele usa um loop para encontrar linhas de uma tabela menor e exclui da tabela menor.
msgisme
O plano para o código ad-hoc (bom) faz uma varredura de tabela da tabela pequena (que possui apenas de 5 a 10 linhas) e usa um índice não agrupado da tabela grande para descobrir quais linhas ele precisa verificar na PK da mesa grande. Vou tentar fazer os planos reais o mais rápido possível.
msgisme

Respostas:

5

Suspeitos comuns:

  1. constantes em adhoc, parâmetros no código
  2. incompatibilidade de tipos de dados no código
  3. cheirar parâmetro

Ponto 1: o otimizador pode escolher o melhor plano para as constantes.
Mude as constantes = mude o plano. Uma plenária parametrizada é recuperável

O ponto 2 introduzirá conversões implícitas devido à precedência do tipo de dados,
por exemplo, coluna varchar em comparação com o parâmetro nvarchar

Ponto 3: use mascaramento de parâmetro ou OPTIMIZE FOR UNKNOWN
Edit: Para testar: execute proc armazenado, execute sp_updatestats, execute novamente. Isso invalidará os planos em cache, o que é melhor do que limpar o cache do plano

Edit: após o comentário de jcolebrand

Você pode desativar o sniffing de várias maneiras. Os 3 principais são

  • RECOMPLEM. Isso é IMO bobo.
  • OTIMIZAR (SIC) PARA DESCONHECIDO
  • Máscara de parâmetro

Máscara de parâmetro:

DECLARE @MaskedParam varchar(10)
SELECT @MaskedParam = @SignaureParam

SELECT...WHERE column = @MaskedParam

O mascaramento e a dica OPTIMIZE têm o mesmo efeito (talvez por razões diferentes). Ou seja, o otimizador precisa usar estatísticas e distribuição de dados ( Nota: ainda em teste por Mark Storey-Smith ) avaliar os parâmetros por seus próprios méritos ? , e não como eles foram a última chamada. O otimizador pode recompilar ou não. O SQL Server 2005 adicionou a recompilação no nível da instrução para que houvesse menos impacto

Agora, por que um plano com parâmetros "sniffed" é "pegajoso" comparado a parâmetros mascarados / "desconhecidos", não tenho certeza.

Eu uso o mascaramento de parâmetros desde o SQL Server 2000 para todos, exceto o código mais simples. Observei que é provável que isso aconteça com código mais complexo. E no meu antigo emprego, tenho alguns procedimentos de relatório que podem alterar os padrões dos parâmetros do plano. Eu acho que a abordagem do "culto à carga" foi mais fácil do que uma ligação de suporte.

Edit 2, 12 Out 2011, depois de algum bate-papo

  • O mascaramento de parâmetro e OPTIMIZE FOR UNKNOWN têm o mesmo efeito, pelo que sei.
    A dica é mais limpa do que o mascaramento, mas foi adicionada ao SQL Server 2008.

  • A detecção de parâmetros ocorre no momento da 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 de SO abaixo)

  • Bob Beauchemin também menciona

Questões pendentes

  • O sniffing ainda se aplica a WITH RECOMPILE? Ou seja, se o otimizador souber descartar o plano, ele pretende reutilizá-lo?

  • Por que os planos sniffed são "pegajosos"?

Links de SO:

gbn
fonte
1. O parâmetro no sp é uma variável no código 2. Novamente, o mesmo tipo de dados 3. Executei ambos com uma grande variedade de parâmetros e obtenho o mesmo plano a cada vez. Limpei o cache após cada tentativa.
msgisme
11
Re: ponto 3. Você também pode executar a instrução com OPTION (RECOMPILE)ou todo o processo WITH RECOMPILEpara forçar o SQL Server a desconsiderar os planos existentes.
Nick Chammas 04/10
3
Aliás, é OPTIMIZEporque a Microsoft é uma empresa americana. :)
Nick Chammas 4/04
11
@Gbn: alguma opinião sobre a prevenção / derrota de cheirar parâmetros?
jcolebrand
11
@jcolebrand: resposta simples é "não" :-)
gbn
2

Não esqueça que as configurações ANSI que você definiu para a conexão planejam uma função na seleção do plano de execução. Quando o aplicativo chama o procedimento armazenado, provavelmente possui configurações ANSI diferentes da sua conexão SSMS.

Mrdenny
fonte