我正在优化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
列上添加索引,并在num
和tid
上都添加索引。服务器未选择两个索引。
我希望减少页面读取次数,但仍会检索id
和num
列。
答案 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
上有一个非聚集索引,您将要查询使用非聚簇索引过滤数据并获得id
和num
列的数据。
因此,SQL Server将使用非聚集索引来过滤数据(t_id=587
),但是在过滤数据之后,SQL Server需要获取存储在id
和num
列中的值。显然,因为您具有聚集索引,所以SQL Server将使用该索引来获取存储在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具有两个独立的集合:一个是从非聚集索引树中获得的结果,另一个是聚集索引树中的数据。
根据这个故事,问题很明显!如果我们对SQL Server说不进行密钥查找会怎样?这将导致SQL Server以较短的方式执行查询(无需键查找,显然也不需要嵌套循环连接!)。
要实现此目的,我们需要在NonClustered索引树中的INCLUDE
列num
中,因此在这种情况下,该索引的叶将包含id
列的数据以及num
列的数据!显然,当我们说SQL Server使用NonClustred Index查找数据并返回id
和num
列时,它将不需要进行键查找!
最后,我们要做的是在非聚集索引中INCLUDE
num
!感谢@MJH的回答:
CREATE NONCLUSTERED INDEX idx ON tblTest1 (t_id)
INCLUDE (num)
幸运的是,SQL Server 2005为NonClustered索引提供了一项新功能,能够在NonClustered索引的叶级中包含其他非键列!
了解更多:
但是如果我们这样编写查询会发生什么?
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需要执行索引搜索(非集群)以找到t_id = 587,然后使用“聚簇索引搜索”从聚簇索引中获取数据。在这种情况下,我们不会获得任何明显的性能改进。
注意:使用索引时,需要制定适当的计划来维护它们。随着索引的碎片化,它们对查询性能的影响将减少,一段时间后您可能会遇到性能问题!您需要制定适当的计划,以在它们分散后重新组织和重建它们!