Existe uma maneira de fazer uma verificação nula em uma variável em uma cláusula WHERE apenas ocorrer uma vez?

12

Eu tenho uma consulta em uma tabela grande que se parece com isso:

declare @myIdParam int = 1

select * 
from myTable
where (@myIdParam is null or myTable.Id = @myIdParam)

Existem vários condicionais semelhantes como este na cláusula where e também há muitas junções, mas este é um resumo.

Efetivamente, se @myIdParam for nulo, não queremos restringir os resultados usando esse parâmetro.

Eu não sou um profissional de banco de dados, mas pelos meus testes parece que essa verificação NULL é feita para todos os registros e não é otimizada de forma alguma.

Se eu remover a verificação nula e supor que o parâmetro não é nulo, a consulta retornará instantaneamente. Caso contrário, leva até dez segundos.

Existe uma maneira de otimizar isso para que a verificação seja feita apenas uma vez em tempo de execução?

Mystagogue
fonte
1
Veja esta resposta: stackoverflow.com/questions/3415582/… tl; dr useOPTION(RECOMPILE)
vercelli
@vercelli isso faz o truque. Considerando que essa pergunta é realmente sobre parâmetros opcionais, eu diria que é uma duplicata da que você vinculou.
Mystagogue
Provavelmente, mas é um post de 6 anos atrás. Talvez com o SqlServer 2014 ou 2016 haja uma nova abordagem. (Eu testei-o em 2014, sem recompilação e tomou para sempre)
Vercelli
Como sua consulta atual possui muitos parâmetros opcionais, o SQL dinâmico fornecerá o melhor desempenho. Veja sommarskog.se/dyn-search.html para obter um artigo completo sobre o assunto.
Dan Guzman
O @DanGuzman usando WITH RECOMPILE, conforme descrito na pergunta vercelli vinculada, reduziu o tempo de consulta de menos de um minuto para praticamente instantâneo, com critérios altamente seletivos. Considero essa a melhor opção para equilibrar desempenho e legibilidade.
Mystagogue

Respostas:

8

Uma maneira é usar o SQL dinâmico, usando uma verificação nula para adicionar opcionalmente essa parte da cláusula where.

declare @myIdParam int = 1
declare @vc_dynamicsql varchar(max)

set @vc_dynamicsql = 'select * from myTable where 1=1'

if @myIdParam is not null
    set @vc_dynamicsql = @vc_dynamicsql + ' and  myTable.Id = @myIdParam'

EXECUTE sp_executesql @vc_dynamicsql
Mystagogue
fonte
2
Eu realmente preferiria não fazer isso, mas é uma solução. Minha esperança é que alguém venha com uma muito melhor.
Mystagogue
1
Essa é a melhor maneira de lidar com essa classe de consulta de pesquisa. A resposta do stackoverflow referida por @vercelli contém ótimas referências de como fazer isso.
Max Vernon
Este é o melhor método, mas notei que o parâmetro @params sp_ExecuteSQLestá ausente e o @vc_dynamicsqlparâmetro precisa ser a NVARCHAR.
James Anderson
4

Sempre que você coloca uma função em torno de uma coluna `ISNULL (@var, table.col) ', por exemplo, você remove a capacidade do SQL de usar um índice. Essa é realmente a opção com melhor desempenho se você deseja mantê-la em uma única consulta.

@var IS NULL or @var = table.col

Caso contrário, você tem duas opções. A primeira é a SQL dinâmica e a resposta do @ Mystagogue é suficiente para que, caso contrário, você possa fazer duas consultas como esta:

IF @var is NULL
     SELECT * FROM table
ELSE
     SELECT * FROM table WHERE @var = col

Nesse formato e no SQL dinâmico, você obterá um plano de consulta diferente para cada uma das consultas (o que potencialmente produzirá melhor desempenho).

Kenneth Fisher
fonte
O SQL na pergunta não está usando ISNULL ou qualquer outra função.
Mystagogue
@MystagogueI eu estava fazendo referência a uma resposta agora excluída.
26916 Kenneth Fisher
0

Bem, você pode:

declare @myIdParam int = 1;

select *
from myTable
where nullif(@myIdParam, myTable.Id) is null;

No entanto, lembre-se de que a nullif()função é essencialmente um invólucro case. Não é uma bala de prata que elimina magicamente ORe, portanto, acelera a consulta.

Roger Wolf
fonte
o uso de funções em uma cláusula where tem um impacto negativo no desempenho, porque impede o uso de índices (ou pelo que ouvi dizer) #
308
@ Mystagogue, sim - geralmente torna as condições de pesquisa não SARGable. Infelizmente, é a única maneira de saber responder à sua pergunta sem recorrer ao SQL dinâmico ou a vários UNIONs. Quando tive essa tarefa exata, escolhi o SQL dinâmico.
Roger Lobo