Mic*_*ael 7 performance sql-server execution-plan sql-server-2012 query-performance
我有以下查询:
DECLARE @p__linq__0 UNIQUEIDENTIFIER
SET @p__linq__0 = '... some guid ...'
SELECT TOP 1
[EventId] AS [EventId],
[DateCreated] AS [DateCreated],
[LocationId] AS [LocationId],
[SourceName] AS [SourceName],
[SourceState] AS [SourceState],
[Priority] AS [Priority],
[EventDescription] AS [EventDescription],
[FirstTrigger] AS [FirstTrigger]
FROM [dbo].[Watchdog]
WHERE
[LocationId] = @p__linq__0
AND
[FirstTrigger] = 1
ORDER BY [DateCreated] DESC
Run Code Online (Sandbox Code Playgroud)
Watchdog 表定义了 2 个 indecies:
EventId主键列上的聚集索引DateCreated列上的非聚集索引阅读有关如何消除键查找的其他文章,我添加了另一个非聚集索引,其中包括来自SELECT
CREATE NONCLUSTERED INDEX [LocationId_FirstTrigger] ON [dbo].[Watchdog]
(
[LocationId] ASC,
[FirstTrigger] ASC
)
INCLUDE ( [EventId],
[DateCreated],
[SourceName],
[SourceState],
[Priority],
[EventDescription]) WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
GO
Run Code Online (Sandbox Code Playgroud)
但是,这没有帮助,实际执行计划是一样的。如果我查看键查找,输出实际上包含在新添加的非聚集索引中。
我的问题是,为什么它仍然在做key lookup而不是索引扫描/搜索?
更新
按照评论中的一些建议,我删除了新创建的非聚集索引,而是在DateCreated包含来自SELECT.
现在执行计划如下:
查询执行时间也从 1+ 分钟减少到几秒(该表有 18+ 百万行)。
这是否意味着由于ORDER BY非聚集索引而完成了键查找?
我的问题是,为什么它仍在执行键查找而不是索引扫描/搜索?
查询指定结果应按 排序DateCreated。由于您已经有一个非聚集索引DateCreated,优化器决定执行键查找的成本低于对所有数据进行排序的成本DateCreated。
这是否意味着由于非聚集索引上的 ORDER BY 而完成了键查找?
基本上,是的。据估计,以所需的顺序读取数据并通过键查找获取任何其他字段会更便宜*,而不是从单个索引中读取所有字段,然后按DateCreated.
您可以通过比较两者之间的估计成本来确认这一点
索引提示应该是这样的FROM:
FROM [dbo].[Watchdog] WITH (INDEX (LocationId_FirstTrigger))
Run Code Online (Sandbox Code Playgroud)
这应该会生成一个没有键查找的计划(因为LocationId_FirstTrigger覆盖了该查询)和一个Sort运算符。我预计“估计成本”会更高,因此选择了另一个计划。
* 在这里解释优化器的选择:
在TOP (1)您的查询手段优化设置一个排的目标,这意味着该计划向快速生成一列面向。优化器期望从索引扫描中LocationId非常快速地找到与您的谓词匹配的一行,因为它假定值是均匀分布的。这在现实中可能是也可能不是。一个成本键查找下面的索引扫描是非常小的。
因此,对于优化器来说,扫描 + 查找选项看起来比使用LocationId_FirstTrigger和排序查找匹配更便宜。您可以通过添加OPTION (QUERYTRACEON 4138)提示来关闭查询的行目标逻辑作为测试。您可能会发现优化器然后选择LocationId_FirstTrigger没有索引提示的索引。
尽管如此,最好的选择还是按照 Mikael Eriksson 的建议修改您的索引。
您应该将该列移动DateCreated到索引中的关键列LocationId_FirstTrigger。
然后你会得到一个寻求LocationId和FirstTrigger与指数的反向扫描,以查找与最高值的行DateCreated根据select top(1) ... order by DateCreated desc。
CREATE NONCLUSTERED INDEX [LocationId_FirstTrigger] ON [dbo].[Watchdog]
(
[LocationId] ASC,
[FirstTrigger] ASC,
[DateCreated]
)
INCLUDE (
[EventId],
[SourceName],
[SourceState],
[Priority],
[EventDescription]) WITH (STATISTICS_NORECOMPUTE = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
如果索引不是按DateCreatedSQL Server排序的,则必须读取所有匹配的行并对它们进行排序以知道要返回的行。