(Pergunta movida do SO)
Eu tenho uma tabela (dados fictícios) com índice clusterizado contém 2 colunas:
Agora eu executo essas duas consultas:
declare
@productid int =1 ,
@priceid int = 1
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid OR @productid IS NULL)
AND (priceid = @priceid OR @priceid IS NULL)
SELECT productid,
t.priceID
FROM Transactions AS t
WHERE (productID = @productid)
AND (priceid = @priceid)
O plano de execução real para ambas as consultas é:
Como você pode ver, o primeiro está usando SCAN enquanto o segundo está usando SEEK.
No entanto - adicionando OPTION (RECOMPILE)
à primeira consulta , fez o plano de execução também usar SEEK :
Amigos no bate-papo do DBA me disseram que:
Em sua consulta, @productid=1, o que significa que (productID=@productID OU @productID IS NULL) pode ser simplificado para (productID=@productID). O primeiro requer uma varredura para funcionar com qualquer valor de @productID, o último pode usar uma busca. Então, quando você usa RECOMPILE, o SQL Server vai olhar qual valor você realmente tem em @productID e fazer o melhor plano para isso. Com um valor não nulo em @productID, uma busca é melhor. Se o valor de @productID for desconhecido, o plano deve se adequar a qualquer valor possível em @productID, o que exigiria uma varredura. Esteja avisado: OPTION (RECOMPILE) forçará uma recompilação do plano toda vez que você executá-lo, o que adicionará alguns milissegundos a cada execução. Embora isso seja apenas um problema se a consulta for executada com muita frequência.
Também :
Se @productID for nulo, qual valor você procuraria? Resposta: não há nada para buscar. Todos os valores se qualificam.
Eu entendo que OPTION (RECOMPILE)
força o SQL Server a ver quais valores reais os parâmetros têm e ver se ele pode SEEK com ele.
Mas agora perco o benefício da compilação antecipada.
Pergunta
IMHO-SCAN só ocorrerá se um parâmetro for nulo.
Tudo bem - deixe o SQL SERVER criar um plano de execução para SCAN.
MAS se o SQL Server vê que eu executo essa consulta muitas vezes com valores: 1,1
, então por que ele não cria OUTRO plano de execução e usa SEEK para isso?
AFAIK-SQL cria plano de execução para as consultas mais acessadas .
Por que o SQL SERVER não salva um plano de execução para:
@productid int =1 , @priceid int = 1
(Eu corro muitas vezes com esses valores)
- É possível forçar o SQL a manter esse plano de execução (que usa SEEK) - para invocação futura?
Resumindo alguns dos principais pontos de nossa discussão na sala de bate-papo :
De um modo geral, o SQL Server armazena em cache um único plano para cada instrução . Esse plano deve ser válido para todos os possíveis valores de parâmetros futuros .
Não é possível armazenar em cache um plano de busca para sua consulta, porque esse plano não seria válido se, por exemplo, @productid fosse nulo.
Em alguma versão futura, o SQL Server pode oferecer suporte a um único plano que escolhe dinamicamente entre uma varredura e uma busca, dependendo dos valores dos parâmetros de tempo de execução, mas isso não é algo que temos hoje.
Classe de problema geral
Sua consulta é um exemplo de um padrão conhecido como consulta "pega tudo" ou "pesquisa dinâmica". Existem várias soluções, cada uma com suas vantagens e desvantagens. Nas versões modernas do SQL Server (2008+), as principais opções são:
IF
blocosOPTION (RECOMPILE)
sp_executesql
O trabalho mais abrangente sobre o assunto é provavelmente o de Erland Sommarskog, incluído nas referências no final desta resposta. Não há como fugir das complexidades envolvidas, por isso é necessário investir algum tempo experimentando cada opção para entender os trade-offs em cada caso.
IF
blocosPara ilustrar uma
IF
solução de bloco para o caso específico da questão:Isso contém uma instrução separada para os quatro possíveis casos nulos ou não nulos para cada um dos dois parâmetros (ou variáveis locais), portanto, há quatro planos.
Há um problema potencial com a detecção de parâmetros, que pode exigir uma
OPTIMIZE FOR
dica em cada consulta. Consulte a seção de referências para explorar esses tipos de sutilezas.recompilar
Conforme observado acima e na pergunta, você também pode adicionar uma
OPTION (RECOMPILE)
dica para obter um novo plano (busca ou varredura) em cada invocação. Dada a frequência relativamente lenta de chamadas no seu caso (uma vez a cada dez segundos, em média, com um tempo de compilação abaixo de um milissegundo), parece provável que esta opção seja adequada para você:Também é possível combinar recursos das opções acima de forma criativa, para aproveitar ao máximo as vantagens de cada método, minimizando as desvantagens. Realmente não há atalho para entender essas coisas em detalhes e, em seguida, fazer uma escolha informada apoiada por testes realistas.
Leitura adicional
RECOMPILE
Opções