Answers:
如果列不在中WHERE/JOIN/GROUP BY/ORDER BY
,而仅在SELECT
子句中的列列表中。
该INCLUDE
子句将数据添加到最低/叶子级别,而不是索引树中。这使索引变小,因为它不是树的一部分
INCLUDE columns
不是索引中的关键列,因此它们没有顺序。这意味着如上所述,它对于谓词,排序等并不是真正有用。但是,如果在关键列的几行中有剩余查找,这可能会很有用。
SELECT
而对于某些则不是呢?\
您可以使用INCLUDE将一或多个列添加到非聚集索引的叶级,如果这样做,则可以“覆盖”查询。
假设您需要查询员工的ID,部门ID和姓氏。
SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5
如果您碰巧在(EmployeeID,DepartmentID)上具有非聚集索引,则在找到给定部门的员工后,现在必须进行“书签查找”以获取实际的完整员工记录,而只是获取“姓氏”列。如果您找到很多员工,那么就绩效而言可能会非常昂贵。
如果您在索引中包含该姓氏:
CREATE NONCLUSTERED INDEX NC_EmpDep
ON Employee(EmployeeID, DepartmentID)
INCLUDE (Lastname)
那么您需要的所有信息都可以在非聚集索引的叶级中找到。只需查找非聚集索引并找到给定部门的员工,您便拥有了所有必要的信息,不再需要为索引中找到的每个员工进行书签查找->这样可以节省大量时间。
显然,您不能在每个非聚集索引中包括每一列-但是,如果您确实有一些查询丢失了仅一到两列以“被覆盖”(并且被大量使用),那么将那些包含在内将非常有帮助转换为合适的非聚集索引。
JOIN
查询中的键上,并且INCLUDE
s必须是您要检索但不能排序的数据。
这个讨论没有在重要的一点上遗漏了:问题不是“非关键列”最好包含为索引列还是包含列。
问题是,使用包含机制包含索引中实际上不需要的列有多昂贵?(通常不属于where子句,但通常包含在select中)。因此,您的困境始终是:
其中:id1,id2 ... idN是经常在限制中使用的列,而col1,col2 ... colN是经常选择但在限制中通常不使用的列
(将所有这些列作为索引键的一部分的选择总是很愚蠢的(除非它们也用于限制条件)-由于维护索引总是很昂贵的,因为即使必须对索引进行更新和排序,也是如此。 “键”没有更改)。
那么使用选项1或2?
答案:如果您的表很少更新-通常是插入到表中/从表中删除,则使用include机制包含一些“热列”(通常在选择中使用-但在限制上不经常使用)相对便宜,因为插入/删除无论如何都需要对索引进行更新/排序,因此,在已经更新索引的同时,存储一些额外的列就不会产生多少额外的开销。开销是用于在索引上存储冗余信息的额外内存和CPU。
如果您考虑添加为包含列的列经常被更新(而没有索引键,则列被更新)- 或 -如果列太多以至于索引接近表的副本,请使用选项1我建议!另外,如果添加某些include-column结果没有任何性能差异-您可能希望跳过添加它们的想法:)验证它们是否有用!
键中每个相同值的平均行数(id1,id2 ... idN)也可能很重要。
请注意,如果在限制中使用一列(作为包含的索引-column添加):只要可以使用这样的索引(基于对索引键 -columns的限制),则SQL Server匹配对索引(叶节点值)的列限制,而不是绕表本身进行昂贵的操作。
很好解释了原因(包括索引叶级中的数据)的原因。您对此进行两点动摇的原因是,当您运行查询时,如果没有包含附加列(SQL 2005中的新功能),则SQL Server必须转到聚簇索引以获取附加列当新的数据页被加载到内存中时,这将花费更多的时间,并增加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
假设情况并非如此,我们col2
在INCLUDE
子句中使用它,因为将其包含在索引的树部分中没有任何好处。
快进几年。
您需要调整此查询:
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添加another_column
到索引的关键部分的末尾(在之后col1
)相当安全。破坏任何东西的风险很小:
DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);
该索引将变大,仍然存在一些风险,但是与引入新索引相比,扩展现有索引通常更好。
如果您有一个不带索引的索引INCLUDE
,那么您将无法知道通过another_col
在其后添加会中断哪些查询Col1
。
CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)
如果another_col
在Col1
和之间加,会发生什么Col2
?其他查询会受到影响吗?
如果您添加INCLUDE
vs.关键列还有其他“好处”,只是为了避免从表中获取它们。但是,我认为文档方面是最重要的方面。
要回答您的问题:
在确定是否创建包含或不包含INCLUDE子句的覆盖索引时,您将建议什么准则?
如果仅出于在索引中可用该列而不访问该表的目的,则将其添加到索引中,请将其放入INCLUDE
子句中。
如果将列添加到索引键带来了其他好处(例如,order by
因为或因为它可以缩小读取索引范围),请将其添加到键中。
您可以在此处阅读有关此内容的更长的讨论:
https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes
内联到索引定义中的所有列的总大小是有限的。话虽这么说,我从来不需要创建那么宽的索引。对我而言,更大的优势是您可以使用一个包含列的索引来覆盖更多查询,因为它们不必以任何特定顺序进行定义。认为是索引内的一个索引。一个示例是StoreID(其中StoreID是低选择性的,这意味着每个商店都与很多客户相关联),然后是客户统计数据(姓,名,名):如果您仅按以下顺序内嵌这些列(StoreID,LastName) ,FirstName,DOB),则只能有效地搜索您知道StoreID和LastName的客户。
另一方面,在StoreID上定义索引并包括LastName,FirstName,DOB列实际上将使您在StoreID上执行两个seeks-index谓词,然后在任何包含的列上寻找谓词。只要它以StoreID开头,就可以覆盖所有可能的搜索排列。