我有一个运行此语句的存储过程
UPDATE QP.LocalMessage WITH (UPDLOCK, ROWLOCK)
SET StatusId = 0
WHERE StatusId = 1 AND DateLockedUntil <= @DateNow
我们已为此更新创建了一个快速运行的索引
CREATE NONCLUSTERED INDEX [IX_LocalMessage_ProcessingLocked]
ON [QP].[LocalMessage] ([DateLockedUntil] ASC)
WHERE ([StatusId] = (1))
但是在查看正在使用的执行计划后,我们没有使用此索引,而是使用聚簇索引扫描。这需要至少在2秒内执行此语句,但我们看到时间达到12-15秒。我想理解为什么它忽略了索引。
答案 0 :(得分:2)
使用非聚簇索引时,SQL Server需要执行密钥查找操作。在select语句中,可以通过覆盖查询来避免这种情况,在更新语句中,这是无法避免的。
如果谓词返回的记录数很大,那么进行聚簇扫描比使用非聚簇索引查找和键查找更好。
您的查询已参数化,因此SQL Server无法确定将返回的记录数。您可以更新整个表格。
如果某些执行只更新了几条记录并且可以使用非聚集索引查找,那么您可以使用&#34;配置重新编译&#34;来配置您的过程。条款。这将允许SQL Server分析您的过程接收的每个参数值的计划。另一种选择是使用&#34;选项(重新编译)&#34;在你的查询中。
但是,请记住,如果要更新的记录数很少,SQL Server将仅使用非聚簇索引。
你也可以通过使用&#34;(index = yourindexname)&#34;来证明我所说的一切。作为一个查询提示,除了你的表名,并分析统计信息和统计时间,以注意它在查询中使用非聚集索引搜索时有多糟糕,它不应该这样做。
答案 1 :(得分:2)
如果您include (StatusId)
,那么您的查询可以使用您的过滤索引。
CREATE NONCLUSTERED INDEX [IX_LocalMessage_ProcessingLocked_Include]
ON [QP].[LocalMessage] ([DateLockedUntil] ASC)
include (StatusId)
WHERE ([StatusId] = (1))
dbfiddle:http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=816af692f8993ce8844b5b5c7182f7a1
参考: