【问题标题】:Why a coverage filtered index do a Lookup为什么覆盖过滤索引执行查找
【发布时间】:2016-09-21 17:32:14
【问题描述】:

我有一个表格,需要每分钟检查一次,以便在总数大于数字时发出警报。

 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

但是看执行计划,有key lookup。

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

谁能解释一下为什么?

【问题讨论】:

  • 你的感觉是对的。这是没有意义的,不应该在逻辑上被要求。但这是一个从未修复过的优化器限制。你可以在这里投票connect.microsoft.com/SQLServer/feedback/details/454744/…
  • @MartinSmith 但这里的情况是因为 ProcessedAt 是与被索引的 CreatedAt 列不同的列,要完成该条件“d.ProccessedAt 为空”,它必须使用该值。因为该优化器必须进行键查找以获得适当的值。您提到的错误是仅当索引列和谓词列相同时..我还没有检查行为..
  • 不,你错过了重点。从逻辑上讲,它根本没有必须 进行键查找来获取该列。它正在检查的条件已经由过滤的索引条件保证。对于索引中的所有行,条件都为真。从逻辑上讲,它可以像 OP 怀疑的那样简单地跳过检查它。存在查找的唯一原因是优化器当前不包含该逻辑,但可以而且应该添加它。
  • 是的,你在这个例子中是正确的,我将在我的答案中添加另一个例子,如果条件发生变化,它会进行密钥查找

标签: sql-server lookup filtered-index


【解决方案1】:

如果条件为 NULL,那么记录集将只有一个值,为什么需要对该值的索引?它将在什么基础上进行排序?因此,您需要在处理后的索引不为空的情况下进行过滤索引,并在代码中使用该条件会有所帮助

您需要在创建索引脚本的 INCLUDED 列中包含 ProcessedAt 列

添加一个示例来解释@Martin Smith 的评论:

表格脚本:

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

如果我通过增加 +5 或 10 来更改条件,那么它会 keyLookup 到聚集索引

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

我只是试图解释这一点。如果条件发生变化,那么它使用 KeyLookup 来获取。在某种程度上,如果该列是可空的并且它具有空值那么它是不同的,那么你是正确的

【讨论】:

  • 在 Process 表中可能有很多行 ProcessedAt = NULL,每行都有不同的 CreatedAt。这个索引的目的是只在这个查询中使用并且速度很快,因为只需要检查那些 ProcessedAt 为空的,我认为过滤索引可能是一个很好的解决方案。
  • 知道了,现在只检查您的索引列是否为“CreatedAt”。键查找是因为对于选定的 createdAt 值,它必须检查 ProcessedAt 列是否为空,所以它确实键查找,如果您在“包含列”中包含 ProcessedAt,您将进入索引扫描本身
  • 我以为添加过滤器,引擎会实现而不需要列。谢谢!!!
猜你喜欢
  • 2010-10-11
  • 1970-01-01
  • 2017-01-12
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 2011-06-19
  • 2013-04-25
  • 2012-07-22
相关资源
最近更新 更多