O índice SEEK não é usado, a menos que OPTION (RECOMPILE)?

11

(Pergunta movida de SO)

Eu tenho uma tabela (dados fictícios) com índice clusterizado contém 2 colunas:

insira a descrição da imagem aqui

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 é:

insira a descrição da imagem aqui

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:

insira a descrição da imagem aqui

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,1entã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?

Script de tabela de criação completo + dados

Royi Namir
fonte
2
Vamos continuar esta discussão no chat .
ypercubeᵀᴹ

Respostas:

10

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 blocos
  • OPTION (RECOMPILE)
  • SQL dinâmico usando 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 blocos

Para ilustrar uma IFsolução em bloco para o caso específico da pergunta:

IF @productid IS NOT NULL AND @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid
        AND T.priceID = @priceid;
END;
ELSE IF @productid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.productID = @productid;
END;
ELSE IF @priceid IS NOT NULL
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T
    WHERE
        T.priceID = @priceid;
END;
ELSE
BEGIN
    SELECT 
        T.productID,
        T.priceID
    FROM dbo.Transactions AS T;
END;

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 FORdica 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ê:

SELECT
    T.productID,
    T.priceID
FROM dbo.Transactions AS T
WHERE
    (T.productID = @productid OR @productid IS NULL)
    AND (T.priceID = @priceid OR @priceid IS NULL)
OPTION (RECOMPILE);

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

Paul White 9
fonte