Database
 sql >> Base de Dados >  >> RDS >> Database

Sniffing de parâmetros, incorporação e opções de RECOMPILE

Sniffing de parâmetros


A parametrização de consultas promove a reutilização de planos de execução em cache, evitando compilações desnecessárias e reduzindo o número de consultas ad-hoc no cache de planos.

Tudo isso são coisas boas, desde que a consulta que está sendo parametrizada realmente deve usar o mesmo plano de execução em cache para diferentes valores de parâmetro. Um plano de execução eficiente para um valor de parâmetro pode não ser uma boa escolha para outros valores de parâmetros possíveis.

Quando a detecção de parâmetros está habilitada (o padrão), o SQL Server escolhe um plano de execução com base nos valores de parâmetros específicos que existem no momento da compilação. A suposição implícita é que as instruções parametrizadas são mais comumente executadas com os valores de parâmetro mais comuns. Isso parece bastante razoável (até óbvio) e, de fato, geralmente funciona bem.

Um problema pode ocorrer quando ocorre uma recompilação automática do plano em cache. Uma recompilação pode ser acionada por vários motivos, por exemplo, porque um índice usado pelo plano armazenado em cache foi descartado (uma correção recompilação) ou porque as informações estatísticas foram alteradas (uma optimidade recompilar).

Seja qual for a causa exata da recompilação do plano, há uma chance de que um atípico value está sendo passado como parâmetro no momento da geração do novo plano. Isso pode resultar em um novo plano em cache (com base no valor de parâmetro atípico sniffed) que não é bom para a maioria das execuções para as quais será reutilizado.

Não é fácil prever quando um plano de execução específico será recompilado (por exemplo, porque as estatísticas mudaram suficientemente), resultando em uma situação em que um plano reutilizável de boa qualidade pode ser substituído de repente por um plano bastante diferente otimizado para valores de parâmetros atípicos.

Um desses cenários ocorre quando o valor atípico é altamente seletivo, resultando em um plano otimizado para um pequeno número de linhas. Esses planos geralmente usam execução de thread único, junções de loops aninhados e pesquisas. Problemas sérios de desempenho podem surgir quando esse plano é reutilizado para valores de parâmetros diferentes que geram um número muito maior de linhas.


Desativando a detecção de parâmetros


A detecção de parâmetros pode ser desabilitada usando o sinalizador de rastreamento documentado 4136. O sinalizador de rastreamento também é compatível com por consulta use através do QUERYTRACEON dica de consulta. Ambos se aplicam a partir do SQL Server 2005 Service Pack 4 (e um pouco antes se você aplicar atualizações cumulativas ao Service Pack 3).

A partir do SQL Server 2016, a detecção de parâmetros também pode ser desabilitada no nível do banco de dados , usando o PARAMETER_SNIFFING argumento para ALTER DATABASE SCOPED CONFIGURATION .

Quando a detecção de parâmetros está desabilitada, o SQL Server usa a distribuição média estatísticas para escolher um plano de execução.

Isso também soa como uma abordagem razoável (e pode ajudar a evitar a situação em que o plano é otimizado para um valor de parâmetro excepcionalmente seletivo), mas também não é uma estratégia perfeita:um plano otimizado para um valor 'médio' pode acabar sendo seriamente abaixo do ideal para os valores de parâmetros comumente vistos.

Considere um plano de execução que contém operadores que consomem memória, como classificações e hashes. Como a memória é reservada antes do início da execução da consulta, um plano parametrizado com base nos valores médios de distribuição pode ser transferido para tempdb para valores de parâmetros comuns que produzem mais dados do que o otimizador esperava.

As reservas de memória geralmente não podem aumentar durante a execução da consulta, independentemente da quantidade de memória livre que o servidor possa ter. Certos aplicativos se beneficiam ao desativar a detecção de parâmetros (consulte esta postagem de arquivo da Equipe de desempenho do Dynamics AX para obter um exemplo).

Para a maioria das cargas de trabalho, desabilitar completamente a detecção de parâmetros é a solução errada , e pode até ser um desastre. A detecção de parâmetros é uma otimização heurística:funciona melhor do que usar valores médios na maioria dos sistemas, na maioria das vezes.

Dicas de consulta


O SQL Server fornece uma variedade de dicas de consulta e outras opções para ajustar o comportamento da detecção de parâmetros:
  • O OPTIMIZE FOR (@parameter = value) a dica de consulta cria um plano reutilizável com base em um valor específico.
  • OPTIMIZE FOR (@parameter UNKNOWN) usa estatísticas de distribuição média para um parâmetro específico.
  • OPTIMIZE FOR UNKNOWN usa distribuição média para todos os parâmetros (mesmo efeito do sinalizador de rastreamento 4136).
  • O WITH RECOMPILE A opção de procedimento armazenado compila um novo plano de procedimento para cada execução.
  • A OPTION (RECOMPILE) dica de consulta compila um novo plano para uma instrução individual.

A velha técnica de “ocultação de parâmetros” (atribuir parâmetros de procedimento a variáveis ​​locais e referenciar as variáveis ​​em vez disso) tem o mesmo efeito que especificar OPTIMIZE FOR UNKNOWN . Pode ser útil em instâncias anteriores ao SQL Server 2008 (o OPTIMIZE FOR dica era nova para 2008).

Pode-se argumentar que todo instrução parametrizada deve ser verificada quanto à sensibilidade aos valores de parâmetro e deixada sozinha (se o comportamento padrão funcionar bem) ou explicitamente sugerida usando uma das opções acima.

Isso raramente é feito na prática, em parte porque realizar uma análise abrangente para todos os valores de parâmetros possíveis pode ser demorado e requer habilidades bastante avançadas. quando ocorrem na produção.

Essa falta de análise prévia é provavelmente uma das principais razões pelas quais o sniffing de parâmetros tem uma má reputação. Vale a pena estar ciente do potencial de problemas e realizar pelo menos uma análise rápida em declarações que provavelmente causarão problemas de desempenho quando recompiladas com um valor de parâmetro atípico.

O que é um parâmetro?


Alguns diriam que um SELECT declaração referenciando uma variável local é uma “instrução parametrizada” das sortes, mas essa não é a definição que o SQL Server usa.

Uma indicação razoável de que uma instrução usa parâmetros pode ser encontrada observando as propriedades do plano (consulte os Parâmetros guia no Sentry One Plan Explorer. Ou clique no nó raiz do plano de consulta no SSMS, abra as Propriedades janela e expanda a Lista de parâmetros nó):



O 'valor compilado' mostra o valor sniffed do parâmetro usado para compilar o plano em cache. O 'valor de tempo de execução' mostra o valor do parâmetro na execução específica capturada no plano.

Qualquer uma dessas propriedades pode estar em branco ou ausente em diferentes circunstâncias. Se uma consulta não for parametrizada, as propriedades simplesmente estarão ausentes.

Só porque nada é simples no SQL Server, há situações em que a lista de parâmetros pode ser preenchida, mas a instrução ainda não está parametrizada. Isso pode ocorrer quando o SQL Server tenta uma parametrização simples (discutida posteriormente), mas decide que a tentativa é “insegura”. Nesse caso, os marcadores de parâmetro estarão presentes, mas o plano de execução não está de fato parametrizado.

Sniffing não é apenas para procedimentos armazenados


A detecção de parâmetros também ocorre quando um lote é parametrizado explicitamente para reutilização usando sp_executesql .

Por exemplo:
EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'K%';

O otimizador escolhe um plano de execução com base no valor sniffed do @NameLike parâmetro. Estima-se que o valor do parâmetro "K%" corresponda a poucas linhas no Product tabela, então o otimizador escolhe uma junção de loop aninhada e uma estratégia de pesquisa de chave:





Executando a instrução novamente com um valor de parâmetro de “[H-R]%” (que corresponderá a muito mais linhas) reutiliza o plano parametrizado em cache:
EXECUTE sys.sp_executesql
    N'
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
    ',
    N'@NameLike nvarchar(50)',
    @NameLike = N'[H-R]%';





A AdventureWorks banco de dados de amostra é muito pequeno para tornar isso um desastre de desempenho, mas esse plano certamente não é ideal para o segundo valor de parâmetro.

Podemos ver o plano que o otimizador teria escolhido limpando o cache do plano e executando a segunda consulta novamente:





Com um número maior de correspondências esperadas, o otimizador determina que uma junção de hash e uma agregação de hash são as melhores estratégias.

Funções T-SQL


A detecção de parâmetros também ocorre com funções T-SQL, embora a maneira como os planos de execução são gerados possa tornar isso mais difícil de ver.

Há boas razões para evitar funções escalares e de várias instruções T-SQL em geral, portanto, apenas para fins educacionais, aqui está uma versão de função com valor de tabela de várias instruções T-SQL de nossa consulta de teste:
CREATE FUNCTION dbo.F
    (@NameLike nvarchar(50))
RETURNS @Result TABLE
(
    ProductID   integer NOT NULL PRIMARY KEY,
    Name        nvarchar(50) NOT NULL,
    TotalQty    integer NOT NULL
)
WITH SCHEMABINDING
AS
BEGIN
    INSERT @Result
    SELECT
        P.ProductID,
        P.Name,
        TotalQty = SUM(TH.Quantity)
    FROM Production.Product AS P
    JOIN Production.TransactionHistory AS TH
        ON TH.ProductID = P.ProductID
    WHERE
        P.Name LIKE @NameLike
    GROUP BY
        P.ProductID,
        P.Name;
 
    RETURN;
END;

A consulta a seguir usa a função para exibir informações para nomes de produtos que começam com 'K':
SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Ver o sniffing de parâmetros com uma função incorporada é mais difícil porque o SQL Server não retorna um plano de consulta pós-execução (real) separado para cada invocação de função. A função pode ser chamada muitas vezes em uma única instrução e os usuários não ficariam impressionados se o SSMS tentasse exibir um milhão de planos de chamada de função para uma única consulta.

Como resultado dessa decisão de design, o plano real retornado pelo SQL Server para nossa consulta de teste não é muito útil:



No entanto, existem maneiras de ver o sniffing de parâmetros em ação com funções incorporadas. O método que escolhi para usar aqui é inspecionar o cache do plano:
SELECT
    DEQS.plan_generation_num,
    DEQS.execution_count,
    DEQS.last_logical_reads,
    DEQS.last_elapsed_time,
    DEQS.last_rows,
    DEQP.query_plan
FROM sys.dm_exec_query_stats AS DEQS
CROSS APPLY sys.dm_exec_sql_text(DEQS.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DEQS.plan_handle) AS DEQP
WHERE
    DEST.objectid = OBJECT_ID(N'dbo.F', N'TF');



Esse resultado mostra que o plano de função foi executado uma vez, a um custo de 201 leituras lógicas com tempo decorrido de 2891 microssegundos, e a execução mais recente retornou uma linha. A representação do plano XML retornada mostra que o valor do parâmetro era cheirou:





Agora execute a instrução novamente, com um parâmetro diferente:
SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'[H-R]%') AS Result;

O plano pós-execução mostra que 306 linhas foram retornadas pela função:



A consulta de cache do plano mostra que o plano de execução em cache para a função foi reutilizado (execution_count =2):



Ele também mostra um número muito maior de leituras lógicas e um tempo decorrido mais longo em comparação com a execução anterior. Isso é consistente com a reutilização de loops aninhados e plano de pesquisa, mas para ter certeza absoluta, o plano de função pós-execução pode ser capturado usando Eventos estendidos ou o SQL Server Profiler ferramenta:





Como a detecção de parâmetros se aplica a funções, esses módulos podem sofrer as mesmas alterações inesperadas no desempenho comumente associadas a procedimentos armazenados.

Por exemplo, na primeira vez que uma função é referenciada, pode ser armazenado em cache um plano que não usa paralelismo. As execuções subsequentes com valores de parâmetro que se beneficiariam do paralelismo (mas reutilizam o plano serial em cache) mostrarão um desempenho inesperadamente ruim.

Esse problema pode ser difícil de identificar porque o SQL Server não retorna planos pós-execução separados para chamadas de função, como vimos. Usando Eventos estendidos ou Perfil para capturar rotineiramente os planos pós-execução pode ser extremamente intensivo em recursos, então muitas vezes faz sentido usar essa técnica de uma forma muito direcionada. As dificuldades em torno da depuração de problemas de sensibilidade de parâmetros de função significam que vale ainda mais a pena fazer uma análise (e codificar defensivamente) antes que a função atinja a produção.

A detecção de parâmetros funciona exatamente da mesma maneira com funções escalares definidas pelo usuário T-SQL (a menos que em linha, no SQL Server 2019 em diante). As funções com valor de tabela em linha não geram um plano de execução separado para cada chamada, porque (como o nome diz) elas são alinhadas na consulta de chamada antes da compilação.

Cuidado com NULLs farejados


Limpe o cache do plano e solicite uma estimativa (pré-execução) plano para a consulta de teste:
SELECT
    Result.ProductID,
    Result.Name,
    Result.TotalQty
FROM dbo.F(N'K%') AS Result;

Você verá dois planos de execução, sendo o segundo para a chamada de função:



Uma limitação da detecção de parâmetros com funções incorporadas em planos estimados significa que o valor do parâmetro é detectado como NULL (não “K%”):



Nas versões do SQL Server anteriores a 2012, este plano (otimizado para um NULL parâmetro) é armazenado em cache para reutilização . Isso é lamentável, porque NULL é improvável que seja um valor de parâmetro representativo e certamente não foi o valor especificado na consulta.

O SQL Server 2012 (e posterior) não armazena em cache os planos resultantes de uma solicitação de “plano estimado”, embora ainda exiba um plano de função otimizado para um NULL valor do parâmetro em tempo de compilação.

Parametrização simples e forçada


Uma instrução T-SQL ad hoc contendo valores literais constantes pode ser parametrizada pelo SQL Server, seja porque a consulta se qualifica para parametrização simples ou porque a opção de banco de dados para parametrização forçada está habilitada (ou um guia de plano é usado para o mesmo efeito).

Uma instrução parametrizada dessa maneira também está sujeita a sniffing de parâmetro. A consulta a seguir se qualifica para parametrização simples:
SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Heidestieg Straße 8664';

O plano de execução estimado mostra uma estimativa de 2,5 linhas com base no valor do parâmetro farejado:



Na verdade, a consulta retorna 7 linhas (a estimativa de cardinalidade não é perfeita, mesmo quando os valores são rastreados):



Neste ponto, você pode estar se perguntando onde está a evidência de que essa consulta foi parametrizada e o valor do parâmetro resultante foi detectado. Execute a consulta uma segunda vez com um valor diferente:
SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';

A consulta retorna uma linha:



O plano de execução mostra que a segunda execução reutilizou o plano parametrizado que foi compilado usando um valor sniffed:


Parametrização e sniffing são atividades separadas


Uma instrução ad-hoc pode ser parametrizada pelo SQL Server sem valores de parâmetros sendo rastreados.

Para demonstrar, podemos usar o sinalizador de rastreamento 4136 para desabilitar o sniffing de parâmetros para um lote que será parametrizado pelo servidor:
DBCC FREEPROCCACHE;
DBCC TRACEON (4136);
GO
SELECT
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE
    A.AddressLine1 = N'Heidestieg Straße 8664';
GO
SELECT 
    A.AddressLine1, 
    A.City, 
    A.PostalCode 
FROM Person.Address AS A 
WHERE 
    A.AddressLine1 = N'Winter der Böck 8550';
GO
DBCC TRACEOFF (4136);

O script resulta em instruções parametrizadas, mas o valor do parâmetro não é rastreado para fins de estimativa de cardinalidade. Para ver isso, podemos inspecionar o cache do plano:
WITH XMLNAMESPACES
    (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
    DECP.cacheobjtype,
    DECP.objtype,
    DECP.usecounts,
    DECP.plan_handle,
    parameterized_plan_handle =
        DEQP.query_plan.value
        (
            '(//StmtSimple)[1]/@ParameterizedPlanHandle',
            'NVARCHAR(100)'
        )
FROM sys.dm_exec_cached_plans AS DECP
CROSS APPLY sys.dm_exec_sql_text(DECP.plan_handle) AS DEST
CROSS APPLY sys.dm_exec_query_plan(DECP.plan_handle) AS DEQP
WHERE 
    DEST.[text] LIKE N'%AddressLine1%'
    AND DEST.[text] NOT LIKE N'%XMLNAMESPACES%';

Os resultados mostram duas entradas de cache para as consultas ad-hoc, vinculadas ao plano de consulta parametrizado (preparado) pelo identificador de plano parametrizado.

O plano parametrizado é usado duas vezes:



O plano de execução mostra uma estimativa de cardinalidade diferente agora que a detecção de parâmetros está desabilitada:



Compare a estimativa de 1,44571 linhas com a estimativa de 2,5 linhas usada quando a detecção de parâmetros foi habilitada.

Com o sniffing desativado, a estimativa vem das informações de frequência média sobre a AddressLine1 coluna. Um extrato do DBCC SHOW_STATISTICS a saída para o índice em questão mostra como esse número foi calculado:Multiplicando o número de linhas na tabela (19.614) pela densidade (7,370826e-5) fornece a estimativa de 1,44571 linhas.



Observação: Acredita-se comumente que apenas comparações de inteiros usando um índice único podem se qualificar para parametrização simples. Eu deliberadamente escolhi este exemplo (uma comparação de string usando um índice não exclusivo) para refutar isso.

COM RECOMPILAR e OPÇÃO (RECOMPILAR)


Quando um problema de sensibilidade de parâmetro é encontrado, um conselho comum em fóruns e sites de perguntas e respostas é “usar recompilar” (assumindo que as outras opções de ajuste apresentadas anteriormente não são adequadas). Infelizmente, esse conselho é muitas vezes mal interpretado para significar adicionar WITH RECOMPILE opção para o procedimento armazenado.

Usando WITH RECOMPILE efetivamente nos retorna ao comportamento do SQL Server 2000, onde todo o procedimento armazenado é recompilado em cada execução.

Uma alternativa melhor , no SQL Server 2005 e posterior, é usar a OPTION (RECOMPILE) dica de consulta apenas na instrução que sofre do problema de sniffing de parâmetros. Essa dica de consulta resulta em uma recompilação da instrução problemática só. Os planos de execução para outras instruções dentro do procedimento armazenado são armazenados em cache e reutilizados normalmente.

Usando WITH RECOMPILE também significa que o plano compilado para o procedimento armazenado não é armazenado em cache. Como resultado, nenhuma informação de desempenho é mantida em DMVs, como sys.dm_exec_query_stats .

Usar a dica de consulta significa que um plano compilado pode ser armazenado em cache e as informações de desempenho estão disponíveis nas DMVs (embora sejam limitadas à execução mais recente, somente para a instrução afetada).

Para instâncias executando pelo menos SQL Server 2008 build 2746 (Service Pack 1 com atualização cumulativa 5), ​​usando OPTION (RECOMPILE) tem outra vantagem significativa sobre WITH RECOMPILE :Somente OPTION (RECOMPILE) ativa a Otimização de incorporação de parâmetros .

A otimização de incorporação de parâmetros


A detecção de valores de parâmetro permite que o otimizador use o valor de parâmetro para derivar estimativas de cardinalidade. Ambos WITH RECOMPILE e OPTION (RECOMPILE) resultam em planos de consulta com estimativas calculadas a partir dos valores reais dos parâmetros em cada execução.

A Otimização de incorporação de parâmetros leva este processo um passo adiante. Os parâmetros de consulta são substituídos com valores constantes literais durante a análise da consulta.

O analisador é capaz de simplificações surpreendentemente complexas, e a otimização de consulta subsequente pode refinar ainda mais as coisas. Considere o seguinte procedimento armazenado, que apresenta o WITH RECOMPILE opção:
CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
WITH RECOMPILE
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC;
END;

O procedimento é executado duas vezes, com os seguintes valores de parâmetro:
EXECUTE dbo.P
	@NameLike = N'K%',
	@Sort = 1;
GO
EXECUTE dbo.P
	@NameLike = N'[H-R]%',
	@Sort = 4;

Porque WITH RECOMPILE é usado, o procedimento é totalmente recompilado em cada execução. Os valores dos parâmetros são farejados cada vez, e usado pelo otimizador para calcular as estimativas de cardinalidade.

O plano para a execução do primeiro procedimento está exatamente correto, estimando 1 linha:



A segunda execução estima 360 linhas, muito perto das 366 vistas em tempo de execução:



Ambos os planos usam a mesma estratégia geral de execução:varrer todas as linhas em um índice, aplicando o WHERE predicado de cláusula como residual; calcule o CASE expressão usada no ORDER BY cláusula; e execute uma Classificação N principais no resultado do CASE expressão.

OPÇÃO (RECOMPILAR)


Agora recrie o procedimento armazenado usando uma OPTION (RECOMPILE) dica de consulta em vez de WITH RECOMPILE :
CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    SELECT TOP (5)
        ProductID,
        Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

A execução do procedimento armazenado duas vezes com os mesmos valores de parâmetro de antes produz dramaticamente diferente planos de execução.

Este é o primeiro plano de execução (com parâmetros solicitando nomes começando com “K”, ordenados por ProductID ascendente):



O analisador incorpora os valores de parâmetro no texto da consulta, resultando no seguinte formato intermediário:
SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    'K%' IS NULL
    OR Name LIKE 'K%'
ORDER BY
    CASE WHEN 1 = 1 THEN ProductID ELSE NULL END ASC,
    CASE WHEN 1 = 2 THEN ProductID ELSE NULL END DESC,
    CASE WHEN 1 = 3 THEN Name ELSE NULL END ASC,
    CASE WHEN 1 = 4 THEN Name ELSE NULL END DESC;

O analisador vai além, removendo contradições e avaliando completamente o CASE expressões. Isto resulta em:
SELECT TOP (5)
    ProductID,
    Name
FROM Production.Product
WHERE
    Name LIKE 'K%'
ORDER BY
    ProductID ASC,
    NULL DESC,
    NULL ASC,
    NULL DESC;

Você receberá uma mensagem de erro se tentar enviar essa consulta diretamente ao SQL Server, pois não é permitido ordenar por um valor constante. No entanto, este é o formulário produzido pelo analisador. É permitido internamente porque surgiu como resultado da aplicação da otimização de incorporação de parâmetros . A consulta simplificada facilita muito a vida do otimizador de consultas:



A Verificação de Índice Agrupado aplica o LIKE predicado como um resíduo. A Computação Escalar fornece a constante NULL valores. O Top retorna as primeiras 5 linhas na ordem fornecida pelo Índice agrupado (evitando uma ordenação). Em um mundo perfeito, o otimizador de consulta também removeria o Compute Scalar que define os NULLs , pois não são usados ​​durante a execução da consulta.

A segunda execução segue exatamente o mesmo processo, resultando em um plano de consulta (para nomes que começam com as letras “H” a “R”, ordenados por Name descendente) assim:



Este plano apresenta uma Pesquisa de índice não clusterizado que cobre o LIKE intervalo, um LIKE residual predicado, a constante NULLs como antes, e um Top (5). O otimizador de consulta escolhe executar um BACKWARD varredura de intervalo na Busca de índice para mais uma vez evitar a classificação.

Compare o plano acima com o produzido usando WITH RECOMPILE , que não pode usar a otimização de incorporação de parâmetros :



Este exemplo de demonstração pode ter sido melhor implementado como uma série de IF instruções no procedimento (uma para cada combinação de valores de parâmetro). Isso pode fornecer benefícios de plano de consulta semelhantes, sem incorrer em uma compilação de instrução a cada vez. Em cenários mais complexos, a recompilação em nível de instrução com a incorporação de parâmetros fornecida por OPTION (RECOMPILE) pode ser uma técnica de otimização extremamente útil.

Uma restrição de incorporação


Há um cenário em que usar OPTION (RECOMPILE) não resultará na aplicação da otimização de incorporação de parâmetros. Se a instrução for atribuída a uma variável, os valores de parâmetro não serão incorporados:
CREATE PROCEDURE dbo.P
    @NameLike nvarchar(50),
    @Sort tinyint
AS
BEGIN
    DECLARE
        @ProductID integer,
        @Name nvarchar(50);
 
    SELECT TOP (1)
        @ProductID = ProductID,
        @Name = Name
    FROM Production.Product
    WHERE
        @NameLike IS NULL
        OR Name LIKE @NameLike
    ORDER BY
        CASE WHEN @Sort = 1 THEN ProductID ELSE NULL END ASC,
        CASE WHEN @Sort = 2 THEN ProductID ELSE NULL END DESC,
        CASE WHEN @Sort = 3 THEN Name ELSE NULL END ASC,
        CASE WHEN @Sort = 4 THEN Name ELSE NULL END DESC
    OPTION (RECOMPILE);
END;

Porque o SELECT agora atribui a uma variável, os planos de consulta produzidos são os mesmos de quando WITH RECOMPILE foi usado. Os valores dos parâmetros ainda são rastreados e usados ​​pelo otimizador de consulta para estimativa de cardinalidade e OPTION (RECOMPILE) ainda compila apenas a instrução única, apenas o benefício da incorporação de parâmetros está perdido.