Por que minha cláusula WHERE se beneficia de uma coluna "incluída"?

12

De acordo com esta resposta , a menos que um índice seja criado sobre as colunas usadas para restringir, a consulta não será beneficiada por um índice.

Eu tenho esta definição:

CREATE TABLE [dbo].[JobItems] (
    [ItemId]             UNIQUEIDENTIFIER NOT NULL,
    [ItemState]          INT              NOT NULL,
    [ItemPriority]       INT NOT NULL,
    [CreationTime]       DATETIME         NULL DEFAULT GETUTCDATE(),
    [LastAccessTime]     DATETIME         NULL DEFAULT GETUTCDATE(),
     -- other columns
 );

 CREATE UNIQUE CLUSTERED INDEX [JobItemsIndex]
    ON [dbo].[JobItems]([ItemId] ASC);
 GO

CREATE INDEX [GetItemToProcessIndex]
    ON [dbo].[JobItems]([ItemState], [ItemPriority], [CreationTime])
    INCLUDE (LastAccessTime);
GO

e esta consulta:

UPDATE TOP (150) JobItems 
SET ItemState = 17 
WHERE 
    ItemState IN (3, 9, 10)
    AND LastAccessTime < DATEADD (day, -2, GETUTCDATE()) 
    AND CreationTime < DATEADD (day, -2, GETUTCDATE());

Revisei o plano real e há apenas uma pesquisa de índice com o predicado exatamente como em WHERE- nenhuma "pesquisa de favoritos" adicional a ser recuperada, LastAccessTimemesmo que a última seja apenas "incluída" no índice, não parte do índice.

Parece-me que esse comportamento contradiz a regra de que a coluna deve fazer parte do índice, e não apenas "incluída".

O comportamento que observo é o correto? Como posso saber com antecedência se meus WHEREbenefícios de uma coluna incluída ou precisam que a coluna faça parte do índice?

dente afiado
fonte
Ele ainda pode procurar baseado no ItemStatevalor, no entanto, a busca não será tão eficiente como se o seu Índice foi estruturado da seguinte forma(ItemState, CreationTime, LastAccessTime)
Mark Sinkinson
1
@MarkSinkinson ou apenas(ItemState, CreationTime) INCLUDE (LastAccessTime)
ypercubeᵀᴹ
@sharptooth, a resposta vinculada que você tem não diz que ("a menos que um índice seja construído sobre as colunas usadas para restringir a consulta não se beneficiará de um índice"). Ele diz que um índice ativado (a,b)não é o melhor para uma consulta SELECT a FROM t WHERE b=5;e que um índice ativado (b) INCLUDE (a)é muito melhor.
precisa saber é o seguinte

Respostas:

9

Seu Predicado é diferente do seu Predicado de Procura.

Um Predicado de busca é usado para pesquisar os dados ordenados no índice. Nesse caso, serão realizadas três buscas, uma para cada ItemState em que você está interessado. Além disso, os dados estão na ordem de ItemPriority, para que nenhuma operação "Busca" seja possível.

Porém, antes que os dados sejam retornados, ele verifica todas as linhas usando o Predicado, ao qual me refiro como Predicado Residual. É feito com os resultados do Predicado de busca.

Qualquer coluna incluída não faz parte dos dados solicitados, mas pode ser usada para satisfazer o Predicado Residual, sem ter que fazer a Pesquisa extra.

Você pode ver o material que escrevi sobre Sargability. Verifique se há uma sessão no SQLBits em particular, em http://bit.ly/Sargability

Editar: para mostrar melhor o impacto dos resíduos, execute a consulta usando o não documentado OPTION (QUERYTRACEON 9130), que separará o resíduo em um operador de filtro separado (que na verdade é uma versão anterior do plano antes que o resíduo seja movido para o operador de busca). Ele mostra claramente o impacto de uma busca ineficaz pelo número de linhas sendo deixadas para o filtro.

Também vale a pena notar que, devido à cláusula IN no ItemState, os dados que estão sendo deixados na verdade estão na ordem ItemState, não na ordem ItemPriority. Um índice composto no ItemState seguido por uma das datas (por exemplo (ItemState, LastAccessTime)) pode ser usado para ter três buscas (observe que o Predicado de busca mostra três buscas no operador de busca), cada uma contra dois níveis, produzindo dados que são ainda na ordem ItemState (por exemplo, ItemState = 3 e LastAccessTime menor que algo, então ItemState = 9 e LastAccessTime menor que algo e, em seguida, ItemState = 10 e LastAccessTime menor que algo).

Um índice em (ItemState, LastAccesTime, CreationTime) não seria mais útil do que um em (ItemState, LastAccessTime) porque o nível CreationTime só será útil se sua Busca for para uma combinação específica ItemState e LastAccessTime, e não um intervalo. Como a lista telefônica não está na ordem do nome, se você estiver interessado em sobrenomes que começam em F.

Se você deseja um índice composto, mas nunca poderá usar as colunas posteriores nos Seek Predicates, devido à maneira como usa as colunas anteriores, também poderá tê-las como colunas incluídas, onde elas ocupam menos espaço no index (porque eles são armazenados apenas no nível da folha do índice, não nos níveis mais altos), mas ainda podem evitar pesquisas e se acostumar nos predicados residuais.

De acordo com o termo Predicado Residual - esse é o meu próprio termo para esta propriedade de uma Busca. Um Merge Join o chama explicitamente de Predicado Residual equivalente, e o Hash Match o chama de Probe Residual (que você pode obter da TSA se corresponder a hash). Mas, em uma Busca, eles chamam de Predicado, o que faz com que pareça menos ruim do que é.

Rob Farley
fonte
3

GetItemToProcessIndex não é totalmente procurável porque sua cláusula where está ativada ItemState + LastAccessTime + CreationTime. As colunas indexadas e a cláusula where não são uma combinação perfeita.

Se você criar um índice de cobertura ItemState + LastAccessTime + CreationTime, para cada correspondência obtida em GetItemToProcessIndex, também obtém o valor da sua Chave Primária (ItemId). Ele só precisa garantir que a segunda data seja uma partida.

É tudo o que você precisa para ir para o local da linha em sua página e atualizá-lo.

Com o índice atual, pode ajudar o servidor a encontrar linhas com o ItemState desejado, mas ainda assim precisa ler todas elas no índice para encontrar correspondências corretas no LastAccessTime + CreationTime. Dependendo dos predicados de data e do tamanho do conjunto de correspondências e do que deve ser excluído, pode resultar em muito mais IO do que um índice de cobertura perfeita nas 3 colunas apenas que buscariam o ItemState e a segunda coluna (1ª data indexada) . A segunda data no indexado pode ser incluída. Colunas extras não devem ser indexadas entre essas 3, embora possa estar ok como uma quarta coluna (consulte a resposta de Rob sobre colunas extras).

Julien Vavasseur
fonte