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

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

——或——

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

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


当前回答

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

其他回答

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就会根据索引(叶-节点-值)匹配列限制,而不是在表本身周围使用昂贵的方法。

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

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

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

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

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

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

您可以使用INCLUDE将一个或多个列添加到非聚集索引的叶级,如果这样做可以“覆盖”您的查询。

假设您需要查询员工的ID、部门ID和姓。

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

如果碰巧在(EmployeeID, DepartmentID)上有一个非聚集索引,一旦找到给定部门的员工,现在必须执行“书签查找”以获得实际的完整员工记录,只是为了获得姓氏列。如果你有很多员工的话,这在绩效方面是非常昂贵的。

如果你在你的索引中包含了这个姓氏:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(DepartmentID)
  INCLUDE (Lastname, EmployeeID)

然后,您需要的所有信息都可以在非聚集索引的叶级中获得。只需在非聚类索引中查找给定部门的员工,就可以获得所有必要的信息,并且不再需要在索引中查找每个员工的书签—>可以节省大量时间。

显然,您不能在每个非聚集索引中包含每一列——但是如果您确实有一些查询只需要“覆盖”一到两列(并且经常使用),那么将这些查询包含到合适的非聚集索引中会非常有帮助。