为什么在SQL索引中使用INCLUDE

Sta*_*ite 14 sql sql-server indexing covering-index

我最近在我维护的数据库中遇到了一个索引:

CREATE INDEX [IX_Foo] ON [Foo]
( Id ASC )
INCLUDE 
( SubId )
Run Code Online (Sandbox Code Playgroud)

在这种特殊情况下,我遇到的性能问题(Id和SubId上的慢速SELECT过滤)可以通过简单地将SubId列移动到索引中而不是作为包含列来修复.

这让我想到了我根本不理解包含列的原因,一般来说,它们可能只是索引本身的一部分.即使我不特别关心索引本身的项目,在索引中使用列而不是简单地包含列也有任何缺点.

经过一些研究,我发现对索引列中的内容有很多限制(索引的最大宽度,以及一些不能像'image'那样索引的列类型).在这些情况下,我可以看到您将被迫在索引页数据中包含该列.

我唯一能想到的是,如果SubId上有更新,如果包含该列,则不需要重新定位该行(尽管索引中的值需要更改).还有别的东西让我失踪吗?

我正在考虑浏览数据库中的其他索引,并尽可能在索引中包含列.这会是一个错误吗?

我主要对MS SQL Server感兴趣,但也欢迎其他数据库引擎的信息.

mar*_*c_s 8

到目前为止,答案都是正确的 - 但是它们可能无法传达你从覆盖指数中获得的足够多.

在你的情况下,你有一个表Foo和一些字段,包括一个Id(我假设是主键),以及SubId一些额外的ID.

您还有一个IX_Foo我认为现在只有Id它的索引.

所以现在你需要找到SubIdfor Id=4.

SELECT Id, SubId
FROM Foo
WHERE Id=4
Run Code Online (Sandbox Code Playgroud)
  • SQL Server将查看SELECT语句并确定它可以使用 IX_Foo
  • 然后它将搜索Id=4索引中的值IX_Foo
  • 当它发现它,它现在需要的价值SubId,也
  • 非聚集索引IX_Foo将包含聚类键值
  • 使用该聚类键值,SQL Server将执行"书签查找"以查找整个数据行所在的实际数据页
  • 它会获取该页面并提取值SubId从它
  • 它将返回这些值以满足您的查询

这里的要点是:一旦SQL Server找到了你Id=4IX_Foo索引,它就需要做另一个I/O操作,一个书签查找,去获取整个数据行,以便能够找到该SubId值.

如果您有覆盖索引(例如IX_Foo还包括)SubId,则会消除执行书签查找的额外I/O. 一旦Id=4IX_Foo索引中找到该值,非聚集索引中的索引页面也将包含值SubId- SQL Server现在可以返回您在SELECT查询中请求的那两个值,无需额外执行(可能很昂贵和因此,慢速)书签查找只是为了获取另一个Id列.

这是覆盖索引的主要好处 - 如果你只需要一个或两个额外的列,除了你正在进行查找的索引值,通过将这些值包含在索引本身中,你可以节省很多书签查找,从而加快了速度.但是,您应该只包含极少数和少量信息 - 不要将整个数据行复制到所有非聚集索引中!这不是重点.

更新:权衡如下:如果你有一个索引(Id,SubId),索引中的所有页面都有两列 - 整个索引树.

如果包含INCIUDE(S​​ubId),则SubId字段仅出现在叶级别.

这意味着

  • SQL Server无法在SubId上搜索和比较(值不在索引树中)
  • 由于值仅在叶级别上,因此使用的空间更少


Joe*_*orn 7

在索引中有一个附加列的原因是,当您执行仅需要索引使用的列的查询时,您可以自己完成索引中的查询.这样您就可以节省一些时间和资源返回到表中.当发生这种情况时,我们说索引是查询的覆盖索引.

您可能不希望将此附加列作为"索引正确"的一部分的原因是因为当您对该列执行插入或更新时,您更可能需要对索引的某些部分进行重新排序.

  • INCLUDEing覆盖列而不是仅仅将它们添加到索引(作为关键字)的另一个好处是,这些列仅添加到索引的叶子中,通常使索引更小且更有效. (5认同)