在准备70-433考试时,我注意到可以用以下两种方法之一创建覆盖索引。

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

——或——

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

INCLUDE条款对我来说很陌生。你为什么要使用它,在决定是否使用INCLUDE子句创建覆盖索引时,你有什么指导方针?


当前回答

如果列不在WHERE/JOIN/GROUP BY/ORDER BY中,而只在SELECT子句中的列列表中使用INCLUDE。

INCLUDE子句将数据添加到最低的/叶级,而不是添加到索引树中。 这使得索引更小,因为它不是树的一部分

INCLUDE列不是索引中的键列,因此它们没有排序。 这意味着它对于我上面提到的谓词、排序等并不是很有用。但是,如果从键列开始的几行中有残留查找,那么它可能会很有用。

另一篇MSDN文章提供了一个实际示例

其他回答

我在已经给出的答案中没有看到的另一个考虑因素是,包含的列可能是不允许作为索引键列的数据类型,例如varchar(max)。

这允许您在覆盖索引中包含这样的列。我最近不得不这样做,以提供一个nHibernate生成的查询,它在SELECT中有很多列,有一个有用的索引。

如果列不在WHERE/JOIN/GROUP BY/ORDER BY中,而只在SELECT子句中的列列表中使用INCLUDE。

INCLUDE子句将数据添加到最低的/叶级,而不是添加到索引树中。 这使得索引更小,因为它不是树的一部分

INCLUDE列不是索引中的键列,因此它们没有排序。 这意味着它对于我上面提到的谓词、排序等并不是很有用。但是,如果从键列开始的几行中有残留查找,那么它可能会很有用。

另一篇MSDN文章提供了一个实际示例

如果您不需要键中的列,那么选择INCLUDE而不是键列的一个原因是文档。这使得在未来发展索引更加容易。

考虑到你的例子:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

如果你的查询是这样的,那么这个索引是最好的:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

当然,如果将列放在关键部分可以获得额外的好处,则不应该将列放在INCLUDE中。下面两个查询实际上都倾向于索引键中的col2列。

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

让我们假设情况并非如此,我们在INCLUDE子句中有col2,因为将它放在索引的树部分中没有任何好处。

几年过去了。

你需要调优这个查询:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

要优化该查询,可以使用以下索引:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

如果你检查你在这个表上已经有哪些索引,你之前的索引可能还在那里:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

现在您知道Col2和Col3不是索引树的一部分,因此不能用于缩小读索引范围,也不能用于对行进行排序。Is在索引的键部分的末尾(在col1之后)添加another_column是相当安全的。打破任何东西的风险很小:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

该指数将变得更大,这仍然有一些风险,但与引入新指数相比,扩展现有指数通常更好。

如果你有一个没有INCLUDE的索引,你不知道在Col1之后添加another_col会中断什么查询。

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

如果在Col1和Col2之间添加another_col会发生什么?其他查询是否会受到影响?

如果添加这些列只是为了避免从表中获取它们,那么INCLUDE与键列相比还有其他“好处”。然而,我认为文档方面是最重要的。

回答你的问题:

在决定是否使用INCLUDE子句创建覆盖索引时,您有什么建议?

如果向索引中添加一列的唯一目的是在不访问表的情况下使该列在索引中可用,请将其放入INCLUDE子句中。

如果将列添加到索引键可以带来额外的好处(例如,通过或因为它可以缩小读索引范围),则将其添加到键中。

你可以在这里阅读更长的讨论:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

基本索引列被排序,但包含的列不被排序。这节省了维护索引的资源,同时仍然可以在所包含的列中提供数据来覆盖查询。因此,如果您想涵盖查询,您可以将搜索条件放在索引的已排序列中定位行,但随后“包括”其他具有非搜索数据的未排序列。它确实有助于减少索引维护中的排序和碎片量。

The reasons why (including the data in the leaf level of the index) have been nicely explained. The reason that you give two shakes about this, is that when you run your query, if you don't have the additional columns included (new feature in SQL 2005) the SQL Server has to go to the clustered index to get the additional columns which takes more time, and adds more load to the SQL Server service, the disks, and the memory (buffer cache to be specific) as new data pages are loaded into memory, potentially pushing other more often needed data out of the buffer cache.