让我们澄清问题,然后讨论改进的解决方案:
您有一个表(我们称之为 tblTest1 并包含 2M 条记录),在 id 上有一个聚集索引,在 t_id 上有一个非聚集索引,您将查询使用过滤数据的数据非聚集索引并获取 id 和 num 列。
所以SQL server会使用Non Clustered Index来过滤数据(t_id=587),但是过滤数据后SQL server需要获取存储在id和num列中的值。显然因为你有聚集索引,那么 SQL 服务器将使用这个索引来获取存储在 id 和 num 列中的数据。发生这种情况是因为非聚集索引树中的叶子包含聚集索引的值,这就是您在执行计划中看到键查找运算符的原因。事实上SQL Server使用Index seek(NonCluster)找到t_id=587然后使用Key Lookup获取num数据!(SQL Server不会使用这个操作符来获取存储在id列中的值,因为您有一个聚集索引,并且非聚集索引中的叶子包含聚集索引的值)。
参考上面的截图,当我们有Index Seek(NonClustred)和Key Lookup时,SQL Server需要Nested Loop Join操作符来获取num列中的数据,使用Index Seek(Nonclustered)操作符。实际上在这个阶段SQL Server有两个独立的集合:一个是从Nonclustered Index树得到的结果,另一个是Clustered Index树里面的数据。
根据这个故事,问题就很清楚了!如果我们对 SQL Server 说,而不是进行 Key Lookup,会发生什么?这将导致 SQL Server 使用更短的方式执行查询(不需要键查找,显然不需要嵌套循环连接!)。
为此,我们需要INCLUDE NonClustered 索引树内的num 列,因此在这种情况下,该索引的叶子将包含id 列的数据以及num 列的数据!显然,当我们说 SQL Server 使用 NonClustred Index 查找数据并返回 id 和 num 列时,它不需要进行 Key Lookup!
最后我们需要做的,是在非聚集索引中INCLUDEnum!感谢@MJH的回答:
CREATE NONCLUSTERED INDEX idx ON tblTest1 (t_id)
INCLUDE (num)
幸运的是,SQL Server 2005 为 NonClustered 索引提供了一项新功能,能够在 NonClustered 索引的叶级包含额外的非键列!
阅读更多:
https://www.red-gate.com/simple-talk/sql/learn-sql-server/using-covering-indexes-to-improve-query-performance/
https://docs.microsoft.com/en-us/sql/relational-databases/indexes/create-indexes-with-included-columns?view=sql-server-2017
但是如果我们这样写查询会发生什么?
SELECT id, num
FROM tblTest1 AS t1
WHERE
EXISTS (SELECT 1
FROM tblTest1 t2
WHERE t2.t_id = 587 AND t2.id = t1.id
)
这是一个很好的方法,但让我们看看执行计划:
显然,SQL server 需要做一次 Index seek(NonClustered) 来找到 t_id=587,然后使用 Clustered Index Seek 从 Clustered Index 中获取数据。在这种情况下,我们不会得到任何显着的性能改进。
注意:当您使用索引时,您需要制定适当的计划来维护它们。随着索引的碎片化,它们对查询性能的影响会降低,一段时间后您可能会遇到性能问题!当它们支离破碎时,您需要制定适当的计划来重组和重建它们!
阅读更多:https://docs.microsoft.com/en-us/sql/relational-databases/indexes/reorganize-and-rebuild-indexes?view=sql-server-2017