2016-09-21 38 views
2

我有一张表,当总数大于一个数字时,需要检查每分钟提醒。为什么覆盖范围筛选索引执行查找

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

我的想法是创建一个过滤索引,是这样的:

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

但看执行计划,有一个键查找。

我不明白为什么,因为索引有两列用于查询。

任何人都可以解释我为什么吗?

+1

你的感觉是正确的。这是没有道理的,不应该逻辑上要求。但这是一个从未被修复的优化器限制。你可以在这里投票https://connect.microsoft.com/SQLServer/feedback/details/454744/filtered-index-not-used-and-key-lookup-with-no-output –

+0

@MartinSmith但是这里的情况是因为ProcessedAt与索引的CreatedAt列不同,为了实现该条件'd.ProccessedAt为null',它必须使用该值。对于该优化器必须执行键查找才能获得适当的值。你提到的错误只有当索引列和谓词列是相同的。我还没有检查行为虽然.. –

+0

不,你已经错过了这一点。它在逻辑上不*具有*进行键查找来获得该列。它正在检查的条件已由过滤的索引条件保证。条件对索引中的所有行都成立。它在逻辑上可以简单地跳过检查它作为OP怀疑。查找的唯一原因是因为优化器当前不包含该逻辑,但它可以并且应该被添加。 –

回答

1

如果条件为NULL,那么您将只有一个值的记录集,为什么你需要一个索引到该值?在什么基础上它会做排序?所以,你需要做的过滤索引,其中,processedAt不为空并使用代码条件将有助于

您需要包括ProcessedAt列在创建索引脚本

包列增加一个例子解释@马丁史密斯评论:

表脚本:

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 
) 

插入到表中的记录:

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 

与不同的列条件创建于id_for_FilteredIndex列非聚集过滤指数another_id

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

如果我查询表完全相同的条件,那么优化器不使用KeyLookup

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

It does use filtered index

如果我通过增加甚至+5或10来改变条件,那么它确实keyLookup到聚集索引

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

我试图解释这只是..如果在条件的变化,然后它使用KeyLookup获取。从某种意义上说,如果列是可以空的,并且它是空值,那么它是不同的

+0

在Process表中,它可能有很多行,ProcessedAt = NULL,每一行都有一个不同的CreatedAt。这个索引的目的只是用在这个查询中,并且速度很快,因为只需要检查ProcessedAt中的那些为null,我认为过滤索引可能是一个好的解决方案。 –

+0

得到你,现在只检查你的索引列是'CreatedAt'。键查找是因为对于选定的createdAt值,它必须检查ProcessedAt列是否为空,因此它会进行键查找,如果包含ProcessedAt在'包含列'中,您将获得索引扫描本身 –

+0

我认为添加过滤器,引擎会意识到并且不需要该列。谢谢!!! –