Forçar o SQL Server a executar as condições de consulta conforme gravadas?

14

Estou usando o SQL Server 2008 R2 e tenho esta pseudo consulta (SP):

select ...
from ...
WHERE    @LinkMode IS NULL
     AND (myColumn IN (...very long-running query...))
     ...
     ...

O problema é que a consulta leva muito tempo para ser executada - mesmo que eu execute o SP com @LinkMode=2.

Como você notou, a consulta de longa execução deve ser executada apenas se @LinkMode for nulo, o que não é o caso aqui. No meu caso, @LinkMode = 2!

No entanto, se eu mudar para:

 select ...
    from ...
    WHERE    1=2
         AND (myColumn IN (...very long time exeted query...))
     ...
     ...

o SP não correr rápido.

Ouvi dizer que às vezes o otimizador pode otimizar a ordem dos critérios.

Então eu pergunto :

  • Mesmo que o otimizador escolha uma rota diferente, o que pode ser mais rápido do que verificar se =null? Quero dizer, acho que a verificação if a==nullé muito mais rápida do que executar a outra consulta longa ...

  • Como forçar o SQL Server a executar a consulta conforme a escrevi (na mesma ordem)?

Royi Namir
fonte

Respostas:

22

Você está caindo na armadilha " Catch-All Query ", que é explicada muito bem por Gail Shaw aqui .

Para resumir o problema: O SQL Server otimiza a sobrecarga significativa da compilação de consultas, armazenando em cache um plano de consulta após a compilação e, posteriormente, verificando o cache em busca de um plano de consulta correspondente antes de uma compilação posterior. A "correspondência" que ocorre aqui é puramente textual; portanto, o valor real de uma variável não afetará isso.

Isso é bom 99% do tempo, mas em alguns casos é ruim . Um caso em que é ruim é quando alguém tenta construir uma cláusula WHERE como se fosse uma instrução IF em C de curto-circuito etc. Isso não funciona bem, porque o compilador SQL precisa fazer um plano de consulta que funcione independentemente do que realmente são os valores dos parâmetros e a única maneira de lidar com essas condições de comutação lógicas "inteligentes" na cláusula WHERE é fazer um plano simples de força bruta que apenas varre a tabela inteira, filtrando as linhas à medida que avançam , sem alavancar nenhum índice.

Não surpreendentemente, isso os torna uniformemente lentos, independentemente dos valores dos parâmetros / variáveis.

RBarryYoung
fonte
8

Não há maneira garantida de forçar o SQL Server a executar suas condições de cláusula em uma sequência específica. O otimizador sempre os avaliará na ordem que achar melhor.

O que você pode fazer é algo como isto:

IF @LinkMode IS NULL
BEGIN
    select ...
    from ...
    WHERE (myColumn IN (...very long time exeted query...))
         ...
         ...
END
ELSE
BEGIN
    select ...
    from ...
    WHERE ...
         ...
END
Nome de tela esotérico
fonte
3

Se for uma opção, use uma instrução SE para executar a forma apropriada da consulta. Além disso, no SQL, você diz ao mecanismo de banco de dados o que fazer, não como fazê-lo - as coisas não são executadas do começo ao fim. Pode ser difícil prever o que exatamente ele fará. Você provavelmente já sabe disso;)

Sam
fonte
2

O SQL dinâmico provavelmente também funcionaria, pois nesse caso o otimizador de consulta deve obter os valores reais em tempo de execução (me corrija se estiver errado, na verdade não tenho certeza, mas pareço lembrar de usá-lo para situações semelhantes) . Mas estou com os outros neste, em que uma cláusula IF / ELSE seria melhor para você, pois é a solução mais simples e fácil que fará exatamente o necessário.

Para referência futura, caso você ainda não o tenha usado, um site horrivelmente feio com um exemplo funcional de SQL dinâmico pode ser encontrado aqui, por exemplo: http://sqlusa.com/bestpractices/dynamicsql/

Kahn
fonte
1

Eu recomendaria a construção IF / ELSE. Se, por qualquer motivo que não funcionar, você sempre poderá usar a opção WITH RECOMPILE.

timvw
fonte
Você poderia elaborar como seria a "construção if / else"? : D
jcolebrand
Eu sugeriria usar OPTION (WITH RECOMPILE), pois isso geraria um plano ideal a cada vez - o atraso da compilação aumentaria a sobrecarga, mas suspeito que seja melhor no geral neste caso.
SqlRyan