Estou lidando com um problema que não consigo resolver. Recebi esse procedimento armazenado que apresenta problemas de velocidade. Demora cerca de 35 segundos. Quando executado adhoc , ele é concluído instantaneamente.
Isso me fez pensar que tinha algo a ver com detecção de parâmetros. Eu tentei reescrever isso para usar variáveis locais, otimizar para desconhecido\variável\etc, com recompilação.
A tabela em que a verificação está acontecendo é enorme, mas mesmo com as estatísticas atualizadas com fullscan
, as estimativas ainda estão MUITO erradas. Talvez a consulta pudesse ser reescrita, mas eu gostaria de descobrir isso.
todos os dados do ambiente de teste com nomes alterados de arquivos de texto .sql para consulta sp e ad hoc - deixe-me saber se isso não funcionar
plano ad hoc - https://www.brentozar.com/pastetheplan/?id=Hkvg5Ge40
Parâmetros para ad hoc -
Plano SP - https://www.brentozar.com/pastetheplan/?id=S1w49GxER
Parâmetros para SP -
Os parâmetros vêm como:
(sp name)
@ TransactionDescriptionId VARCHAR(32),
@ CategoryCode VARCHAR(4000)
AS
DECLARE
@ CategoryHash VARBINARY(16) = HashBytes('MD5', @CategoryCode);
Os mesmos valores são passados tanto na versão SP quanto na versão ad hoc . Achei que poderia ser devido a algo com o hash sendo criado como uma variável local a partir do parâmetro passado, mas na versão ad hoc está fazendo o mesmo (para mim).
Eh
Os dois primeiros métodos listados aqui são bastante inúteis na maioria das vezes para melhorar o desempenho . Você pode se deparar com uma situação a cada 5 a 10 anos em que eles melhoram uma consulta específica, mas eu manteria essas opções bem baixas na minha lista de coisas para tentar.
No plano lento, a parte que consome mais tempo fica assim:
Aparentemente, você tem uma coluna computada na
Catalog_Items
tabela que gera o MD5 e ela não é persistente. Você não precisa persistir, mas a razão pela qual você acaba com um operador Filter em uma expressão e não pode aplicar o predicado enquanto verifica a tabela é porque ele precisa ser calculado por linha em tempo de execução .Na consulta rápida, você tem
CategoryCodeHash
o index[IX_Catalog_Items_IdRetailer_CategoryCodeHash]
, que persiste o valor e o torna pesquisável.Quanto ao motivo pelo qual você obteve uma estimativa tão ruim no plano lento, não está claro se o
@CategoryHash
argumento é um parâmetro ou uma variável local neste contexto, mas...O fato de
@TransactionDescriptionId
possuir oParameterCompiledValue
atributo e@CategoryHash
não parece indicar que não é um parâmetro detectado. No plano rápido nenhum dos dois tem oParameterCompiledValue
atributo, mas sem a reprodução completa não sei exatamente o que você fez de diferente.de qualquer forma
O otimizador baseado em custos do SQL Server achou a busca + pesquisa mais cara do que a verificação + filtro de índice clusterizado ou nunca chegou ao plano de busca + pesquisa. Muitas pessoas dirão para você "fazer a cobertura do índice" para que todas as colunas necessárias estejam disponíveis no
[IX_Catalog_Items_IdRetailer_CategoryCodeHash]
índice. Essa é uma opção, é claro, mas você também pode fazer coisas para enganar o otimizador e fazê-lo usar índices estreitos, como usar um self-join .A ideia é unir uma tabela a si mesma em valores de chave primária, com uma referência à tabela usada para selecionar todas as colunas que deseja exibir, e a outra referência responsável pela união e filtragem e quaisquer outras atividades relacionais.
contextualmente
Uma coisa que pode valer a pena considerar por que o plano é diferente seriam as várias opções de SET . Alguns são necessários para que visualizações indexadas, colunas computadas e índices filtrados sejam avaliados durante a otimização:
O
DatabaseContextSettingsId
é diferente para os dois planos:vs.
Você pode ver algumas das configurações aqui:
Você também pode ver se o banco de dados possui algum padrão que vá contra os requisitos de todos os módulos criados sem nenhuma especificação diferente:
Dependendo do que você vê, você pode tentar recriar o procedimento armazenado com o seguinte:
algumas outras coisas
Para sua consideração geral, embora não seja necessariamente um grande problema para esta consulta específica:
NOLOCK
fede e pode retornar dados incorretos/inconsistentesTOP
sem umORDER BY
é uma proposição duvidosa@Table Variables
forçam modificações para serem executadas em thread único e geralmente não são tão interessantes quando você começa a juntá-las a outras tabelas, pois elas não obtêm estatísticas em nível de coluna, mesmo quando indexadasObserve o
CouldNotGenerateValidParallelPlan
atributo doINSERT
operador. Neste caso, porém, ambos os custos do plano são tão baixos que é improvável que se consiga um plano de execução paralelo de qualquer maneira.