sql server分页慢主因是offset fetch需扫描并丢弃前n行,n越大越慢;应建覆盖索引(含where、order by字段及select列),优先用游标分页替代。

SQL Server 分页查询慢,不是 OFFSET FETCH 写法本身的问题,而是它强制数据库先扫描并丢弃前 N 行——N 越大,性能越差。5000 条就卡到 20 秒,说明没走索引或走了但没覆盖查询字段。
为什么 OFFSET FETCH 在 SQL Server 里会越来越慢
OFFSET 1000 ROWS FETCH NEXT 20 ROWS ONLY 不是“跳到第 1001 行开始取”,而是让 SQL Server 先按 ORDER BY 排出至少 1020 行,再扔掉前 1000 行。数据量一上去,I/O 和内存压力直线上升。
- 即使
ORDER BY id字段有索引,如果WHERE条件没命中索引最左前缀(比如只查name LIKE '%abc%'),就会退化成全表扫描 - SELECT * 会触发大量回表操作——索引只存了排序字段和主键,其他列还得去聚簇索引里捞,放大 I/O
- SQL Server 查询优化器在深分页时可能放弃使用索引,改用更“稳妥”但更慢的计划(尤其当统计信息过期时)
必须建的索引类型和写法
索引不是随便加一个就行。要让 OFFSET FETCH 快起来,索引必须同时支撑 WHERE 过滤、ORDER BY 排序、以及 SELECT 返回的字段。
- 单条件分页(如
WHERE status = 10 ORDER BY time DESC):建CREATE INDEX IX_table_status_time ON 目标表 (status, time) INCLUDE (id, name, other_needed_columns) - 模糊查询分页(如
WHERE status = 10 AND name LIKE '%张%'):name上的 LIKE '%xxx%' 无法用索引前导列加速,所以要把status放最左,再加INCLUDE所有 SELECT 字段 - 避免在同一个索引里混用 ASC/DESC(SQL Server 2019 及之前版本对混合方向支持差,容易导致索引不被选用)
比 OFFSET FETCH 更快的替代方案
真要撑住万级数据分页,别硬扛 OFFSET。游标分页(Keyset Pagination)才是 SQL Server 下更稳的选择。
- 第一页:
SELECT TOP 20 * FROM 目标表 WHERE status = 10 ORDER BY time DESC - 第二页起(假设上一页最后一条
time = '2026-04-20 10:30:00'):SELECT TOP 20 * FROM 目标表 WHERE status = 10 AND time - 注意:必须用
TOP+ 精确边界值,不能用OFFSET;时间字段需有唯一性保障(或联合主键补位),否则会漏/重数据
容易被忽略的执行计划陷阱
加了索引也不一定快——得看执行计划里是不是真用了它。
- 打开 SSMS,按
Ctrl+M开启「显示实际执行计划」,运行你的分页语句,重点看是否出现「Index Scan」(全索引扫描)或「Table Scan」(全表扫描) - 如果看到「Key Lookup」图标,说明索引没覆盖所有 SELECT 字段,正在频繁回表——立刻补
INCLUDE - 检查「Estimated Number of Rows」和「Actual Number of Rows」是否严重偏离,偏离大说明统计信息陈旧,运行
UPDATE STATISTICS 目标表 WITH FULLSCAN
真正卡住的往往不是语法,而是索引设计与查询字段之间的错配。哪怕只少一个 INCLUDE 列,或者 WHERE 条件里多一个未索引字段,都可能让整个分页从毫秒级掉到秒级。动手前,先看执行计划,别猜。










