![](/img/trans.png)
[英]SQL Server : different execution plan and performance for similar queries
[英]SQL SERVER generates different and unoptimized execution plan for two similar queries
使用 SQL SERVER 执行以下两个查询
SELECT TOP(10) [c].[Name] AS [I0], [c].[Surname] AS [I1],(
SELECT MAX([r].[Date])
FROM [Presences].[Regs] AS [r]
WHERE ([c].[Id] = [r].[ColId]) AND [r].[ColId] IS NOT NULL) AS [I7], [c].[Id] AS [I10]
FROM [Presences].[Cols] AS [c]
SELECT TOP(10) [c].[Code] AS [I0], [c].[Description] AS [I1], (
SELECT MAX([r].[Date])
FROM [Presences].[Regs] AS [r]
WHERE ([c].[Id] = [r].[CantId]) AND [r].[CantId] IS NOT NULL) AS [I9], [c].[Id] AS [I10]
FROM [Presences].[Cants] AS [c]
第二个查询的评估时间不到 1 秒,但第一个查询需要 20 多秒。
事实上,第一个生成的执行计划非常不同:
如果与第二个相比:
我不清楚为什么不选择索引搜索。
这些是在表 [Regs] 上声明的索引:
CREATE NONCLUSTERED INDEX [IX_Regs_Date] ON [Presences].[Regs]
(
[Date] ASC
)
CREATE NONCLUSTERED INDEX [IX_Regs_ColId] ON [Presences].[Regs]
(
[ColId] ASC
)
CREATE NONCLUSTERED INDEX [IX_Regs_CantId] ON [Presences].[Regs]
(
[CantId] ASC
)
表 [Cols] 有大约 600 行,表 [Cants] 有 21000。
有趣的事实是以下查询(使用 FORCESEEK)生成了正确的执行计划:
SELECT TOP(10) [c].[Name] AS [I0], [c].[Surname] AS [I1],(
SELECT MAX([r].[Date])
FROM [Presences].[Regs] AS [r]
WITH (FORCESEEK)
WHERE ([c].[Id] = [r].[ColId]) AND [r].[ColId] IS NOT NULL) AS [I8]
FROM [Presences].[Cols] AS [c]
ORDER BY [c].[Name], [c].[Surname]
但我无法指定此提示,因为查询是使用 ORM 生成的。
如果您需要更多信息,我很乐意提供。
您的问题查询是
SELECT TOP(10) c.Name AS I0,
c.Surname AS I1,
(SELECT MAX(r.Date)
FROM Presences.Regs AS r
WHERE ( c.Id = r.ColId )) AS I7,
c.Id AS I10
FROM Presences.Cols AS c
ORDER BY c.Name, c.Surname
在好计划和坏计划中,顶部是相同的
它扫描Cols
表,按Name, Surname
排序,然后继续计算Presences.Regs
中的MAX(Date)
,其中r.ColId
与外行的相应c.Id
匹配。
理想的索引是ColId, Date
上的索引 - 因此它可以查找索引并读取该ColId
的最后一个。
你没有这个索引,所以它有两个选择
IX_Regs_Date
- 这是按日期顺序排列的,因此它可以在找到与c.Id = r.ColId
匹配的第一行后立即停止扫描IX_Regs_ColId
。 获取与谓词匹配的所有行,然后将它们聚合以找到MAX
。 对于选项 1,它估计每次扫描平均只需读取283
行,然后才能找到第一个匹配c.Id = r.ColId
的行。 实际上,每次执行读取1,358,719
次(并且有 10 次执行,因此对应的 1358 万次键查找)。 表中有1,517,230
行,因此看起来好像出于某种原因,所有匹配联接的行都聚集在表中的较晚日期。
解决此问题的最简单方法是为其提供ColId, Date
的理想索引 - 这涵盖了查询,因此即使在“好”计划中也可以删除查找,并且是明显的最佳选择,因此将消除 SQL 的诱惑服务器到 select 的“坏”计划。
--Suggested replacement for IX_Regs_ColId
CREATE NONCLUSTERED INDEX [IX_Regs_ColId_Date] ON [Presences].[Regs]
(
[ColId] ASC,
[Date] ASC
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.