Preciso otimizar uma SELECT
instrução, mas o SQL Server sempre faz uma verificação de índice em vez de uma busca. Esta é a consulta que, é claro, está em um procedimento armazenado:
CREATE PROCEDURE dbo.something
@Status INT = NULL,
@IsUserGotAnActiveDirectoryUser BIT = NULL
AS
SELECT [IdNumber], [Code], [Status], [Sex],
[FirstName], [LastName], [Profession],
[BirthDate], [HireDate], [ActiveDirectoryUser]
FROM Employee
WHERE (@Status IS NULL OR [Status] = @Status)
AND
(
@IsUserGotAnActiveDirectoryUser IS NULL
OR
(
@IsUserGotAnActiveDirectoryUser IS NOT NULL AND
(
@IsUserGotAnActiveDirectoryUser = 1 AND ActiveDirectoryUser <> ''
)
OR
(
@IsUserGotAnActiveDirectoryUser = 0 AND ActiveDirectoryUser = ''
)
)
)
E este é o índice:
CREATE INDEX not_relevent ON dbo.Employee
(
[Status] DESC,
[ActiveDirectoryUser] ASC
)
INCLUDE (...all the other columns in the table...);
O plano:
Por que o SQL Server escolheu uma verificação? Como posso corrigir isso?
Definições de coluna:
[Status] int NOT NULL
[ActiveDirectoryUser] VARCHAR(50) NOT NULL
Os parâmetros de status podem ser:
NULL: all status,
1: Status= 1 (Active employees)
2: Status = 2 (Inactive employees)
IsUserGotAnActiveDirectoryUser pode ser:
NULL: All employees
0: ActiveDirectoryUser is empty for that employee
1: ActiveDirectoryUser got a valid value (not null and not empty)
sql-server
sql-server-2012
index
optimization
Bestter
fonte
fonte
@Status
?Status DESC
? Quantos valores existemStatus
, quais são eles (se o número é pequeno) e cada valor é representado aproximadamente da mesma forma? Mostre-nos a saída deSELECT TOP (20) [Status], c = COUNT(*) FROM dbo.Employee GROUP BY [Status] ORDER BY c DESC;
Respostas:
Não acho que a verificação seja causada por uma pesquisa por uma string vazia (e, embora você possa adicionar um índice filtrado para esse caso, isso ajudará apenas variações muito específicas da consulta). É mais provável que você seja vítima do sniffing de parâmetros e de um único plano não otimizado para todas as várias combinações de parâmetros (e valores de parâmetros) que você fornecerá para esta consulta.
Eu chamo isso de procedimento "pia da cozinha" , porque você espera que uma consulta forneça todas as coisas, incluindo a pia da cozinha.
Eu tenho um vídeo sobre minha solução para isso aqui , mas essencialmente, a melhor experiência que tenho para essas consultas é:
OPTION (RECOMPILE)
- isso evita que valores específicos de parâmetros forcem o tipo errado de plano, especialmente útil quando você tem inclinação de dados, estatísticas incorretas ou quando a primeira execução de uma instrução usa um valor atípico que levará a um plano diferente do que mais tarde e mais frequente execuções.optimize for ad hoc workloads
- isso evita que variações de consulta usadas apenas uma vez poluam o cache do plano.Ative a otimização para cargas de trabalho ad hoc:
Mude seu procedimento:
Depois de ter uma carga de trabalho com base nesse conjunto de consultas que você pode monitorar, você pode analisar as execuções e ver quais delas se beneficiariam mais de índices adicionais ou diferentes - você pode fazer isso de vários ângulos, desde simples "qual combinação de parâmetros são fornecidos com mais frequência? " para "quais consultas individuais têm tempos de execução mais longos?" Não podemos responder a essas perguntas com base apenas no seu código, apenas podemos sugerir que qualquer índice seja útil apenas para um subconjunto de todas as combinações possíveis de parâmetros que você está tentando oferecer suporte. Por exemplo, se
@Status
é NULL, nenhuma busca nesse índice não agrupado é possível. Portanto, nos casos em que os usuários não se importam com o status, você fará uma varredura, a menos que tenha um índice que atenda às outras cláusulas (mas esse índice também não será útil, dada a sua lógica de consulta atual - cadeia vazia ou cadeia vazia não é exatamente seletiva).Nesse caso, dependendo do conjunto de
Status
valores possíveis e da distribuição desses valores,OPTION (RECOMPILE)
pode não ser necessário. Mas se você tiver alguns valores que renderão 100 linhas e alguns que renderão centenas de milhares, convém que ele esteja lá (mesmo com o custo da CPU, que deve ser marginal, dada a complexidade dessa consulta), para que você possa obter buscas no maior número de casos possível. Se o intervalo de valores for finito o suficiente, você pode até fazer algo complicado com o SQL dinâmico, onde diz "Eu tenho esse valor muito seletivo para@Status
; portanto, quando esse valor específico for passado, faça essa ligeira alteração no texto da consulta para que isso é considerado uma consulta diferente e otimizado para esse valor de parâmetro ".fonte
Isenção de responsabilidade : algumas das coisas nesta resposta podem fazer um DBA recuar. Estou abordando isso do ponto de vista do desempenho puro - como obter pesquisas de índice quando você sempre obtém verificações de índice.
Com isso fora do caminho, aqui vai.
Sua consulta é conhecida como "consulta de pia da cozinha" - uma consulta única destinada a atender a várias condições possíveis de pesquisa. Se o usuário definir
@status
um valor, você deseja filtrar esse status. Se@status
estiverNULL
, retorne todos os status e assim por diante.Isso apresenta problemas com a indexação, mas eles não estão relacionados à capacidade de Sargability, porque todas as suas condições de pesquisa são "iguais a" critérios.
Isso é sargável:
Isso não é sargável porque o SQL Server precisa avaliar
ISNULL([status], 0)
para cada linha em vez de procurar um único valor no índice:Recriei o problema da pia da cozinha de uma forma mais simples:
Se você tentar o seguinte, você obterá uma verificação de índice, mesmo que A seja a primeira coluna do índice:
Isso, no entanto, produz uma busca por índice:
Enquanto você estiver usando uma quantidade gerenciável de parâmetros (dois no seu caso), provavelmente poderá fazer
UNION
várias consultas de pesquisa - basicamente todas as permutações dos critérios de pesquisa. Se você tiver três critérios, isso parecerá confuso; com quatro, será completamente incontrolável. Voce foi avisado.Para o terceiro desses quatro usar uma busca de índice, você precisará de um segundo índice
(B, A)
. Veja como sua consulta pode parecer com essas alterações (incluindo minha refatoração da consulta para torná-la mais legível).... mais você precisará de um índice adicional
Employee
com as duas colunas de índice invertidas.Para completar, devo mencionar que
x=@x
implicitamente significa quex
não pode serNULL
porqueNULL
nunca é igual aNULL
. Isso simplifica um pouco a consulta.E sim, a resposta SQL dinâmica de Aaron Bertrand é uma escolha melhor na maioria dos casos (ou seja, sempre que você pode conviver com as recompilações).
fonte
Sua pergunta básica parece ser "Por que" e acho que você pode encontrar a resposta sobre o minuto 55 ou mais desta ótima apresentação de Adam Machanic na TechEd há alguns anos atrás.
Menciono os 5 minutos no minuto 55, mas toda a apresentação vale o tempo. Se você olhar para o plano de consulta da sua consulta, tenho certeza de que encontrará Predicados Residuais para a pesquisa. Basicamente, o SQL não pode "ver" todas as partes do índice porque algumas delas estão ocultas pelas desigualdades e outras condições. O resultado é uma varredura de índice para um superconjunto baseado no Predicado. Esse resultado é colocado em spool e, em seguida, verificado novamente usando o predicado residual.
Verifique as propriedades do Operador de digitalização (F4) e veja se você tem "Seek Predicate" e "Predicate" na lista de propriedades.
Como outros indicaram, é difícil indexar a consulta como está. Eu tenho trabalhado em muitos similares recentemente e cada um deles exigiu uma solução diferente. :(
fonte
Antes de questionarmos se a busca de índice é preferível à varredura de índice, uma regra geral é verificar quantas linhas são retornadas versus o total de linhas da tabela subjacente. Por exemplo, se você espera que sua consulta retorne 10 linhas de 1 milhão de linhas, a busca de índice provavelmente é altamente preferida à varredura de índice. No entanto, se alguns milhares de linhas (ou mais) devem ser retornados da consulta, a busca por índice NÃO pode necessariamente ser preferida.
Sua consulta não é complexa; portanto, se você puder publicar um plano de execução, poderemos ter idéias melhores para ajudá-lo.
fonte
este é apenas o original formatado
esta é a revisão - não tenho 100% de certeza, mas (talvez) experimente,
mesmo uma OU provavelmente será um problema que
poderia ser interrompido no ActiveDirectoryUser null
fonte