在准备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中有很多列,有一个有用的索引。

There is a limit to the total size of all columns inlined into the index definition. That said though, I have never had to create index that wide. To me, the bigger advantage is the fact that you can cover more queries with one index that has included columns as they don't have to be defined in any particular order. Think about is as an index within the index. One example would be the StoreID (where StoreID is low selectivity meaning that each store is associated with a lot of customers) and then customer demographics data (LastName, FirstName, DOB): If you just inline those columns in this order (StoreID, LastName, FirstName, DOB), you can only efficiently search for customers for which you know StoreID and LastName.

另一方面,在StoreID上定义索引并包括LastName、FirstName、DOB列将允许您在本质上进行两次查找——在StoreID上进行索引谓词,然后在任何包含的列上进行查找谓词。这将让你覆盖所有可能的搜索排列,只要它开始与StoreID。

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.

这个讨论忽略了重要的一点:问题不在于“非键列”作为索引列更好,还是作为包含列更好。

问题是使用包含机制来包含索引中并不真正需要的列的代价有多大?(通常不是where-子句的一部分,但通常包含在select中)。所以你的困境总是:

在id1上使用索引,id2…单独idN或 在id1上使用索引,id2…idN + include col1, col2…colN

地点: Id1, id2…idN是常用于限制的列,col1, col2…colN是经常选择的列,但通常不用于限制

(将所有这些列作为index-key的一部分的选项总是愚蠢的(除非它们也用于限制)——因为它总是维护更昂贵,因为即使“键”没有改变,索引也必须更新和排序)。

所以选择1还是2?

Answer: If your table is rarely updated - mostly inserted into/deleted from - then it is relatively inexpensive to use the include-mechanism to include some "hot columns" (that are often used in selects - but not often used on restrictions) since inserts/deletes require the index to be updated/sorted anyway and thus little extra overhead is associated with storing off a few extra columns while already updating the index. The overhead is the extra memory and CPU used to store redundant info on the index.

如果您考虑添加作为包含的列—列经常更新(索引键列没有更新)—或者—如果它们太多以至于索引变得接近您的表的副本—我建议使用选项1 !此外,如果添加某些include-column(s)结果没有产生性能差异-你可能想要跳过添加它们的想法:)验证它们是有用的!

键中每个相同值的平均行数(id1, id2…)idN)也有一定的重要性。

请注意,如果一个列—作为索引的包含列添加—在限制中使用:只要这样的索引可以使用(基于对索引-键-列的限制)—那么SQL Server就会根据索引(叶-节点-值)匹配列限制,而不是在表本身周围使用昂贵的方法。

如果您不需要键中的列,那么选择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