2016-09-21 1 views
2

J'ai une table qui doit vérifier chaque minute pour alerter lorsque le total est supérieur à un nombre.Pourquoi un index filtré de couverture effectue-t-il une recherche

SELECT 
    count(CreatedAt) Total 
FROM 
    Process d 
WHERE 
    d.ProcessedAt is null 
    AND DATEDIFF(second, GETUTCDATE(), d.CreatedAt) > 30 

Mon idée était de créer un index filtré, quelque chose comme:

CREATE NONCLUSTERED INDEX [FIX_Process_CreatedAt_ProcessedAt] ON [dbo].[Process] 
(
    [CreatedAt] ASC 
) 
WHERE ProcessedAt IS NULL 

Mais en regardant le plan d'exécution, il y a une recherche de clé.

Je ne comprends pas pourquoi, car l'index a les deux colonnes pour la requête.

Quelqu'un peut-il m'expliquer pourquoi?

+1

Votre impression est correcte. Cela n'a pas de sens et ne devrait pas être requis logiquement. Mais c'est une limitation d'optimiseur qui n'a jamais été fixée. Vous pouvez voter ici https://connect.microsoft.com/SQLServer/feedback/details/454744/filtered-index-not-used-and-key-lookup-with-no-output –

+0

@MartinSmith Mais ici la situation est parce que ProcessedAt est une colonne différente de la colonne CreatedAt qui est indexée, pour accomplir cette condition 'd.ProccessedAt is null', elle doit utiliser cette valeur. Pour cet optimiseur doit effectuer une recherche de clé pour obtenir la valeur appropriée. Bug que vous avez mentionné est seulement si la colonne indexée et les colonnes de prédicat sont identiques .. Je dois encore vérifier le comportement si .. –

+0

Non, vous avez manqué le point. Logiquement, il n'a pas à faire des recherches de clés pour obtenir cette colonne. La condition qu'il vérifie est déjà garantie par la condition d'index filtrée. La condition est vraie pour toutes les lignes de l'index.Logiquement, il peut simplement sauter la vérification comme le PO soupçonné. La seule raison pour laquelle la recherche est là est parce que l'optimiseur ne contient pas cette logique mais il pourrait et devrait être ajouté. –

Répondre

1

Si la condition est NULL alors vous aurez seulement une valeur pour le jeu d'enregistrements et pourquoi vous avez besoin d'un index à cette valeur? Sur quelle base fera-t-il le tri? Donc, vous devez faire index filtré où processedAt est non nul et utiliser cette condition dans le code aidera

Vous devez inclure la colonne ProcessedAt dans la colonne inclus dans le script de création Index

Ajout d'un exemple pour expliquer @ Martin Smith commentaire:

script de table:

Create Table TestKeyLookup 
(
id int identity(1,1) primary key -- Created PK which will create clustered Index 
,id_for_filteredIndex int NOT NULL 
,another_id int NOT NULL 
) 

Insérer dans les enregistrements de table:

declare @i int = 50 
while @i < 1000000 
begin 
    insert into TestKeyLookup (id_for_filteredIndex, another_id) values (@i, @i+5) 
    set @i = @i + 10 
END 

Créer un index non cluster filtré sur colonne id_for_FilteredIndex avec condition sur différentes colonnes another_id

create nonclustered index NCI_TestKeyLookup on dbo.TestKeyLookup(id_for_filteredIndex) 
where another_id > **673105** 

Si j'Interrogation de la table avec exactement le même état alors optimiseur n'utilise pas keylookup

select count(id_for_filteredIndex) from TestKeyLookup with(index(NCI_TestKeyLookup)) 
where another_id > 673105 

It does use filtered index

Si je change la condition en augmentant même +5 ou 10 alors il fait keyLookup à l'index en cluster

select count(id_for_filteredIndex) from TestKeyLookup with(index(NCI_TestKeyLookup)) 
where another_id > 673110 

Different Id where another_id is greater than filtered index condition

J'essaie d'expliquer ce que .. S'il y a un changement dans l'état, alors il utilise keylookup chercher. D'une certaine manière vous avez raison si la colonne est nullable et qu'elle a des valeurs nulles alors c'est différent

+0

Dans la table de processus il pourrait avoir beaucoup de rangées avec ProcessedAt = NULL, chacune avec un différent CreatedAt. Le but de cet index est d'être utilisé seulement dans cette requête et être rapide, puisque seulement besoin de vérifier ceux avec ProcessedAt est null, j'ai supposé qu'un index filtré pourrait être une bonne solution. –

+0

Vous avez maintenant, vérifiant que votre colonne d'indexation est 'CreatedAt' .. La recherche de clé est parce que pour la valeur createdAt sélectionnée il doit vérifier la colonne ProcessedAt si elle est nulle ou non, donc elle fait la recherche de clé, Si vous incluez ProcessedAt dans 'Inclure la colonne' vous obtiendrez dans l'index lui-même l'analyse –

+0

Je pensais qu'en ajoutant le filtre, le moteur réaliserait et n'a pas besoin de la colonne. Merci!!! –