当数据库强制连接时,如何强制执行更好的执行计划?

How to I force a better execution plan when the database is forcing a join?

我正在优化 SQL Server 2005 上的查询。我有一个针对 mytable 的简单查询,大约有 200 万行:

SELECT id, num
FROM mytable
WHERE t_id = 587

id 字段是主键(聚集索引)并且 t_id 字段上存在非聚集索引。

上述查询的查询计划包括 Clustered Index Seek 和 Index Seek,然后执行 Nested Loop (Inner Join) 来组合结果。 STATISTICS IO 显示 3325 页读取。

如果我将查询更改为以下内容,服务器将只执行 6 次页面读取,并且只执行一次没有连接的索引查找:

SELECT id
FROM mytable
WHERE t_id = 587

我尝试在 num 列上添加索引,并在 numtid 上添加索引。服务器未选择任何索引。

我希望减少页面读取次数,但仍会检索 idnum 列。

以下索引应该是最佳的:

CREATE INDEX idx ON MyTable (t_id)
INCLUDE (num)

我不记得 INCLUDEd 列在 2005 年是否是有效语法,您可能必须使用:

CREATE INDEX idx ON MyTable (t_id, num)

[id] 列将包含在索引中,因为它是聚集键。

最佳索引将在 (t_id, num, id)

您的查询之所以不好,可能是因为选择了多行。我想知道像这样改写查询是否会提高性能:

SELECT t.id, t.num
FROM mytable t
WHERE EXISTS (SELECT 1
              FROM my_table t2
              WHERE t2.t_id = 587 AND t2.id = t.id
             );

让我们澄清问题,然后讨论改进的解决方案:

您有一个 table(我们称之为 tblTest1 并包含 2M 条记录),在 id 上有一个聚簇索引,在 t_id 上有一个非聚簇索引,并且您将查询使用非聚集索引过滤数据并获取 idnum 列的数据。

所以SQL服务器会使用非聚集索引来过滤数据(t_id=587),但是在过滤数据后SQL服务器需要获取存储在[=中的值13=] 和 num 列。显然因为你有聚簇索引然后 SQL 服务器将使用这个索引来获取存储在 idnum 列中的数据。发生这种情况是因为非聚簇索引树中的叶子包含聚簇索引的值,这就是您在执行计划中看到 Key Lookup 运算符的原因。实际上 SQL 服务器使用 Index seek(NonCluster) 找到 t_id=587 然后使用 Key Lookup 获取 num 数据!(SQL 服务器将不要使用此运算符获取存储在 id 列中的值,因为您有一个聚簇索引并且非聚簇索引中的叶子包含聚簇索引的值)。

参考above-mentioned截图,当我们有Index Seek(NonClustred)和一个Key Lookup时,SQL服务器需要一个Nested Loop Join运算符来获取数据num 列使用 Index Seek(Nonclustered) 运算符。实际上在这个阶段SQL Server有两个独立的集合:一个是从非聚集索引树中得到的结果,另一个是聚集索引树中的数据。

基于这个故事,问题就清楚了!如果我们对 SQL 服务器说不进行密钥查找,会发生什么情况?这将导致 SQL 服务器使用更短的方式执行查询(不需要密钥查找,显然不需要嵌套循环连接!)。

为此,我们需要 INCLUDE 非聚集索引树中的 num 列,因此在这种情况下,该索引的叶节点将包含 id 列的数据,并且还有 num 列的数据!很明显,当我们说 SQL 服务器使用 NonClustred Index 和 return idnum 列查找数据时,它不需要进行 Key Lookup!

最后我们要做的,就是INCLUDE NonClustered Index中的num!感谢的回答:

CREATE NONCLUSTERED INDEX idx ON tblTest1 (t_id)
INCLUDE (num)

幸运的是,SQL Server 2005 为非聚集索引提供了一项新功能,能够在非聚集索引的叶级中包含额外的 non-key 列!

阅读更多:

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服务器需要做一个Index seek(NonClustered)来找到t_id=587,然后使用Clustered Index Seek从Clustered Index中获取数据。在这种情况下,我们不会得到任何 notable 性能改进。

注意:当您使用索引时,您需要有一个适当的计划来维护它们。随着索引变得碎片化,它们对查询性能的影响将会降低,一段时间后您可能会遇到性能问题!当它们支离破碎时,您需要有一个适当的计划来重组和重建它们!

阅读更多:https://docs.microsoft.com/en-us/sql/relational-databases/indexes/reorganize-and-rebuild-indexes?view=sql-server-2017