为什么在SQL索引中使用INCLUDE

时间:2010-03-13 01:39:22

标签: sql sql-server indexing covering-index

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

CREATE INDEX [IX_Foo] ON [Foo]
( Id ASC )
INCLUDE 
( SubId )

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:8)

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

在您的情况下,您有一个表Foo和一些字段,包括Id(我假设是主键)和SubId,这是一个额外的ID某种。

你还有一个索引IX_Foo我认为它现在只有Id

现在您需要找到SubId的{​​{1}}。

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

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

如果您有覆盖索引,例如SubId还包括IX_Foo,用于执行书签查找的额外I / O被删除。在SubId索引中找到值Id=4后,非聚集索引中的索引页面也将包含IX_Foo的值 - SQL Server现在可以返回您提出的那两个值在您的SELECT查询没有必须执行额外(可能很昂贵,因此很慢)书签查找只是为了获取另一个Id列。

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

UPDATE:权衡是这样的:如果你有一个索引(Id,SubId),索引中的所有页面都有两列 - 整个索引树通过。

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

这意味着

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

答案 1 :(得分:7)

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

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

答案 2 :(得分:3)

在索引中使用include允许将索引用作覆盖索引(即,可以单独使用该索引来满足某些查询,而无需对聚簇索引执行书签查找),而无需将这些列添加到实际树的一部分索引,从而保持索引的大小。 (包含的列仅添加到索引的叶节点中。)