Twi*_*mes 14 sql-server nonclustered-index bookmark-lookup pagination
我有一个如下表:
create table [Thing]
(
[Id] int constraint [PK_Thing_Id] primary key,
[Status] nvarchar(20),
[Timestamp] datetime2,
[Foo] nvarchar(100)
)
Run Code Online (Sandbox Code Playgroud)
Status在和字段上使用非聚集、非覆盖索引Timestamp:
create nonclustered index [IX_Status_Timestamp] on [Thing] ([Status], [Timestamp] desc)
Run Code Online (Sandbox Code Playgroud)
如果我查询这些行的“页面”,使用偏移/获取如下,
select * from [Thing]
where Status = 'Pending'
order by [Timestamp] desc
offset 2000 rows
fetch next 1000 rows only
Run Code Online (Sandbox Code Playgroud)
我知道该查询需要读取总共 3000 行才能找到我感兴趣的 1000 行。然后我希望它对这 1000 行中的每一行执行键查找以获取索引中未包含的字段。
但是,执行计划表明它正在对所有 3000 行进行键查找。我不明白为什么,当唯一的条件(按[状态]过滤和按[时间戳]排序)都在索引中时。
如果我用 cte 重新表述查询,如下所示,我或多或少会得到我期望第一个查询执行的操作:
with ids as
(
select Id from [Thing]
where Status = 'Pending'
order by [Timestamp] desc
offset 2000 rows
fetch next 1000 rows only
)
select t.* from [Thing] t
join ids on ids.Id = t.Id
order by [Timestamp] desc
Run Code Online (Sandbox Code Playgroud)
来自 SSMS 的一些统计数据用于比较 2 个查询:
| 原来的 | 具有热膨胀系数 | |
|---|---|---|
| 逻辑读 | 12265 | 4140 |
| 子树成本 | 9.79 | 3.33 |
| 内存授予 | 0 | 3584 KB |
乍一看,CTE 版本似乎“更好”,尽管我不知道该对它为工作台提供内存授予的事实给予多少重视。(来自的消息set statistics io on表明工作台上任何类型的读取均为零)
我说第一个查询应该能够首先隔离相关的 1000 行(尽管这需要先读取过去 2000 行其他行),然后只对这 1000 行进行键查找,我这样说是错误的吗?必须尝试使用 CTE 查询“强制”该行为似乎有点奇怪。
(作为第二个问题:我假设 CTE 方法的最后一部分需要order by对连接的结果执行自己的操作,即使 CTE 本身有一个order by,因为在连接期间排序可能会丢失。是这是正确的吗?)
Pau*_*ite 16
从根本上来说,这是一个长期存在的优化器限制。
SQL Server 不考虑将键查找转变为聚集索引查找。键查找必须几乎立即跟随与其关联的非聚集索引访问(由于I/O 原因,可能存在中间排序)。
有多种方法可以重写查询以尽可能长时间地仅对索引键进行操作,而无需引入示例中看到的排序:
WITH IDs AS
(
SELECT T.*
FROM dbo.Thing AS T
WHERE T.[Status] = N'Pending'
ORDER BY T.[Timestamp] DESC
OFFSET 2000 ROWS
FETCH NEXT 1000 ROWS ONLY
)
SELECT
T.*
FROM IDs AS I
JOIN dbo.Thing AS T
ON T.Id = I.Id
ORDER BY
I.[Timestamp] DESC;
Run Code Online (Sandbox Code Playgroud)
或者
SELECT
T2.*
FROM dbo.Thing AS T
JOIN dbo.Thing AS T2
ON T2.Id = T.Id
WHERE
T.[Status] = N'Pending'
ORDER BY
T.[Timestamp] DESC
OFFSET 2000 ROWS
FETCH NEXT 1000 ROWS ONLY;
Run Code Online (Sandbox Code Playgroud)
通过帮助优化器“查看”保留的排序顺序,可以消除排序的需要。
进一步阅读:
| 归档时间: |
|
| 查看次数: |
765 次 |
| 最近记录: |