(Pergunta movida de 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 o SCAN enquanto o segundo está usando o 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:
Na sua consulta, @ productid = 1, o que significa que (productID = @ productID OR @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. Portanto, quando você usa RECOMPILE, o SQL Server analisa qual valor você realmente possui no @productID e faz o melhor plano para isso. Com um valor não nulo em @productID, é melhor procurar. Se o valor de @productID for desconhecido, o plano deverá 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ê o executar, o que adicionará alguns milissegundos a cada execução. Embora isso seja apenas um problema se a consulta for executada com muita frequência.
Além disso :
Se @productID for nulo, qual valor você procuraria? Resposta: não há nada a procurar. Todos os valores são qualificados.
Eu entendo que isso OPTION (RECOMPILE)
força o SQL Server a ver quais valores reais os parâmetros têm e ver se ele pode BUSCAR com ele.
Mas agora eu perco o benefício da compilação à frente.
Questão
IMHO - SCAN só ocorrerá se um parâmetro for nulo.
Tudo bem - deixe o SQL SERVER criar um plano de execução para o SCAN.
MAS, se o SQL Server perceber 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 atingidas .
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 o SEEK) - para invocação futura?
fonte
Respostas:
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 futuros de parâmetros .
Não é possível armazenar em cache um buscam plano para a sua consulta, porque esse plano não seria válida se, por exemplo, @productid é nulo.
Em alguma versão futura, o SQL Server poderá oferecer suporte a um único plano que escolha dinamicamente entre uma verificação e uma procura, 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 chamado de "pegar tudo" ou "pesquisa dinâmica". Existem várias soluções, cada uma com suas próprias 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 tema é provavelmente de Erland Sommarskog, que está incluído nas referências no final desta resposta. Não há como fugir das complexidades envolvidas; portanto, é necessário investir algum tempo na tentativa de cada opção para entender as compensações em cada caso.
IF
blocosPara ilustrar uma
IF
solução em bloco para o caso específico da pergunta:Isso contém uma instrução separada para os quatro casos nulos ou não nulos possíveis para cada um dos dois parâmetros (ou variáveis locais), portanto, existem quatro planos.
Existe um problema em potencial com o sniffing de parâmetros, o que pode exigir uma
OPTIMIZE FOR
dica em cada consulta. Por favor, 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 chamada. 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 milissegundos), parece provável que esta opção seja adequada para você:Também é possível combinar recursos das opções acima de maneiras criativas, 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 depois fazer uma escolha informada, apoiada em testes realistas.
Leitura adicional
RECOMPILE
opçõesfonte