我有一张名为readings的表,其中包含>我正在运行此查询的7600万行:
declare @tunnel_id int = 13
SELECT TOP 1 local_time, recorded_time
FROM readings
WHERE tunnel_id = @tunnel_id
ORDER BY id DESC
id列是bigint,设置为主键,并且具有聚簇索引,并且tunnel_id字段上还有一个索引。
这项工作很棒,并且在不到一秒的时间内回复了20个不同的tunnel_id中的16个我正在尝试。但是,在最后4个左右,查询需要40秒,并使用数十万次读取。
我尝试将查询修改为:
SELECT TOP (1) local_time, recorded_time
FROM readings
where id = (
SELECT TOP 1 id
FROM readings
WHERE tunnel_id = 13
ORDER BY id DESC
)
对于一些tunnel_id来说再次只是缓慢。令我更加困惑的是内部选择快速运行缓慢的id,如果我硬编码最大id而不是子查询,它也会快速运行。
我在这里错过了什么让这个查询表现不佳?
编辑评论:
Tunnel_id不是唯一的,每个隧道有数百万行。这是在Sql Server 2012上运行的。
我包括快速和慢速运行的实际执行计划,它们是相同的。
快速:
慢速:
但是正如你所看到的,第一个在不到一秒的时间内执行,而第二个则需要51秒。
答案 0 :(得分:1)
该计划基本上从头到尾扫描整个聚簇索引,并使用tunnel_id = @tunnel_id查找第一行。
我受过良好教育的猜测是“慢”'隧道在聚集索引的开头没有任何行,因此它必须扫描更多的行。
这个非聚集索引应该加快速度:
CREATE NONCLUSTERED INDEX [IX_FOO] ON [readings]
(
tunnel_id,
ID
)
INCLUDE
(
local_time,
recorded_time
)
这可以替换tunnel_id上的现有索引。
答案 1 :(得分:1)
这里有趣的部分是SQL根本没有在tunnel_id中使用索引,只是整个扫描表,如果它像7600万行一样大,那就慢了。 我认为它不使用它的真正原因是因为它必须执行查找然后进行额外的排序。我一开始怀疑参数嗅探是这里的主要问题。
我会尝试更改索引,并使其覆盖。如果可能的话,在索引中包括本地时间,记录的时间和id(不是100%确定是否需要它,因为它是集群密钥)。
CREATE NONCLUSTERED INDEX IX_tunnel_id ON dbo.readings (tunnel_id) INCLUDE (id, local_time, recorded_time)
请注意,虽然这可以改进此特定查询,但它会使插入和更新速度变慢,并且需要额外的存储空间。
答案 2 :(得分:0)
刚发现你可以提示使用tunnel_id索引:
declare @tunnel_id int = 13
SELECT TOP 1 local_time, recorded_time
FROM readings
WITH (INDEX(idx_tunnel_id))
WHERE tunnel_id = @tunnel_id
ORDER BY id DESC
按预期工作,并在不到1秒的时间内返回。