有一个查询位于cpu最密集列表的顶部,但它的简单性(参见下面的示例)与此相矛盾。然而,"查找丢失的索引"脚本建议为表创建索引…但是这个确切的(列顺序和包含)索引已经存在。
SELECT COUNT(Id)
FROM dbo.ProductOrder
WHERE userId = @userId
AND status = @status
"find missing index"脚本建议在userId和status上建立索引。
我们注意到缺失的索引建议几个星期前,发现一个帖子(某处),这可能是一个错误在SQL Server R2 SP1(我们使用的版本)。
但现在查询是(并保持)在前10名…我不确定了。
我们尝试过的事情:
- 添加Id到包含列表(不建议)
- NOLOCK/READ未提交事务隔离级别
- sp_recompile在表
我们有一个严格的维护计划,确保碎片保持在最低限度。
这里可能发生了什么?
编辑:当我开始这篇文章时,我提到了执行计划,但是执行计划实际上没有提到索引(不再)。"查找丢失的索引"脚本。
编辑2:索引定义CREATE NONCLUSTERED INDEX [ProductOrder_UserStatus_Nidx] ON [dbo].[ProductOrder]
(
[userId] ASC,
[status] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
GO
编辑3:缺少索引输出
statement: dbo.ProductOrder
improvement_measure: 187243,941382055
create_index_statement: CREATE INDEX [missing_index_4_3_ProductOrder] ON [dbo].[ProductOrder] ([userId], [status])
group_handle: 4
unique_compiles: 10
user_seeks: 51161
user_scans: 0
last_user_seek: 2014-06-22 10:06:12.390
last_user_scan: NULL
avg_total_user_cost: 8,77252167199463
avg_user_impact: 41,72
system_seeks: 0
system_scans: 0
last_system_seek: NULL
last_system_scan: NULL
avg_total_system_cost: 0
avg_system_impact: 0
database_id: 8
object_id: 2014122416
更新:新脚本找到正确的查询
我们我们正在查看错误的查询。一个新脚本找到了在查询计划中实际生成缺失索引语句的脚本:
CREATE PROCEDURE [dbo].[ProductOrder_GetByUserAndStatus]
@userId INT,
@status INT,
@deliveryStatus INT
AS
SELECT U.id
, U.email
, U.[language]
, U.username
, U.firstname + ' ' + ISNULL(u.middlename, '') + ' ' + u.lastname AS fullname
, pp.[product]
, pp.[status]
, pp.[deliveryStatus]
FROM [dbo].[ProductOrder] PO
JOIN [dbo].[User] U ON U.[id] = PO.[userId]
AND PO.[pool] = @userId
AND PO.[status] = @status
AND PO.[deliveryStatus] = ISNULL(@deliveryStatus, PO.[deliveryStatus])
RETURN 0
也许SQL服务器丢失了,因为在where子句中实现了可选的过滤器参数的最后一行?
我已经尝试创建更新索引,包括列列表中的deliveryStatus以及include语句(在两个单独的更新中,运行统计和重新编译);第一个解决方案的性能下降(缺少索引仍然在计划中),第二个解决方案没有任何差异。
将此问题作为SQL server中的错误关闭?
有哪些索引呢?希望是userId, status INCLUDE (Id)
。不需要其他任何东西(任何额外的列都会减慢这个查询的速度)。如果Id
不是空的,也不是CI的一部分,你甚至不需要包括它。也许,你的意思是并且应该写COUNT(*)
,因为你可能不关心在Id
中不计数空。
如果您想通过聚集索引将其优化为单行查找(这非常快),请使用按userId, status
分组的索引视图。
锁,与普遍的迷信相反,在这里不太可能被怀疑。事实上,NOLOCK
是相当危险的。