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

时间:2019-06-25 23:18:14

标签: sql-server tsql sql-server-2005 query-performance

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

SELECT id, num
FROM mytable
WHERE t_id = 587

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

上述查询的查询计划既包括聚簇索引查找,也包括索引查找,然后执行嵌套循环(内部联接)以合并结果。 STATISTICS IO显示3325页读取。

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

SELECT id
FROM mytable
WHERE t_id = 587

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

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

3 个答案:

答案 0 :(得分:2)

以下索引应该是最佳的:

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

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

CREATE INDEX idx ON MyTable (t_id, num)

[id]列将作为聚簇键包含在索引中。

答案 1 :(得分:1)

最佳索引应位于(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
             );

答案 2 :(得分:1)

让我们弄清楚问题,然后讨论解决问题的方法:

您有一个表(让它称为tblTest1并包含2M记录),该表的id上有一个聚集索引,t_id上有一个非聚集索引,您将要查询使用非聚簇索引过滤数据并获得idnum列的数据。

因此,SQL Server将使用非聚集索引来过滤数据(t_id=587),但是在过滤数据之后,SQL Server需要获取存储在idnum列中的值。显然,因为您具有聚集索引,所以SQL Server将使用该索引来获取存储在idnum列中的数据。发生这种情况是因为非聚集索引树中的叶子包含聚集索引的值,这就是为什么您在执行计划中看到“键查找”运算符的原因。实际上,SQL Server使用Index seek(NonCluster)来找到t_id=587,然后使用Key Lookup来获取num数据!(SQL Server不会使用此运算符来获取值。存储在id列中,因为您具有聚集索引,并且非聚集索引中的叶子包含聚集索引的值。

enter image description here

参考上面的屏幕快照,当我们有Index Seek(NonClustred)Key Lookup时,SQL Server需要一个Nested Loop Join运算符才能使用以下命令获取num列中的数据Index Seek(Nonclustered)运算符。实际上,在此阶段,SQL Server具有两个独立的集合:一个是从非聚集索引树中获得的结果,另一个是聚集索引树中的数据。

根据这个故事,问题很明显!如果我们对SQL Server说不进行密钥查找会怎样?这将导致SQL Server以较短的方式执行查询(无需键查找,显然也不需要嵌套循环连接!)。

要实现此目的,我们需要在NonClustered索引树中的INCLUDEnum中,因此在这种情况下,该索引的叶将包含id列的数据以及num列的数据!显然,当我们说SQL Server使用NonClustred Index查找数据并返回idnum列时,它将不需要进行键查找!

最后,我们要做的是在非聚集索引中INCLUDE num!感谢@MJH的回答:

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

enter image description here

幸运的是,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
             )

这是一个很好的方法,但是让我们看看执行计划:

enter image description here

很明显,SQL Server需要执行索引搜索(非集群)以找到t_id = 587,然后使用“聚簇索引搜索”从聚簇索引中获取数据。在这种情况下,我们不会获得任何明显的性能改进。

注意:使用索引时,需要制定适当的计划来维护它们。随着索引的碎片化,它们对查询性能的影响将减少,一段时间后您可能会遇到性能问题!您需要制定适当的计划,以在它们分散后重新组织和重建它们!

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