offset fetch 必须紧接 order by 之后,偏移量从0开始,性能随偏移量线性下降,fetch 不支持直接使用变量。

OFFSET FETCH 语法必须写在 ORDER BY 后面
SQL Server 的 OFFSET FETCH 不是独立子句,它依赖 ORDER BY 存在。如果漏写或把 ORDER BY 放在后面,会直接报错:Incorrect syntax near 'OFFSET'。
实操建议:
- 必须先写
ORDER BY column_name,再接OFFSET N ROWS FETCH NEXT M ROWS ONLY - 排序字段最好有索引,否则分页性能会随页码增大明显下降
- 不能只用
FETCH,OFFSET是强制项;也不能省略ONLY(虽然部分版本容忍,但 SQL Server 2019 要求显式写出)
OFFSET 的偏移量从 0 开始,不是第 1 页
很多人误以为第 1 页该写 OFFSET 1 ROWS,结果跳过了首行。实际上 OFFSET 0 ROWS 才是取第 1 页,OFFSET 20 ROWS 表示跳过前 20 行——也就是第 2 页(每页 20 条)的起始位置。
常见错误现象:
- 第 1 页数据缺失:写了
OFFSET 1导致首行被跳过 - 页码计算错乱:后端传入页码
page = 1,却直接代入OFFSET page,应改为OFFSET (page - 1) * size - 前端显示“第 1 页共 100 页”,但最后一页查不到数据:因总记录数不能被每页条数整除,需用
COUNT(*)预查总数,不能仅靠是否返回空结果判断
大偏移量下性能骤降,慎用于深度分页
OFFSET 100000 ROWS 并不等于“直接跳到第 10 万行”,SQL Server 仍需扫描并跳过前 10 万行——即使有索引,I/O 和 CPU 开销也随偏移量线性增长。
使用场景与替代思路:
- 适合前端分页控件的前几页(如 page ≤ 50),用户极少翻到上千页
- 若需支持“跳转到指定页”或后台导出,改用基于游标/键集的分页(例如
WHERE id > @last_id ORDER BY id OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY) - 避免在视图或复杂 CTE 中嵌套
OFFSET FETCH,执行计划可能无法优化排序和跳过逻辑
FETCH NEXT 必须指定具体数值,不能用变量(除非动态 SQL)
直接写 FETCH NEXT @size ROWS ONLY 会报错:Incorrect syntax near '@size'。T-SQL 不允许在 FETCH 中使用参数或变量。
实操建议:
- 硬编码数字:适用于固定分页大小(如后台管理列表始终每页 20 条)
- 拼接动态 SQL:用
sp_executesql安全传参,例如:DECLARE @sql NVARCHAR(MAX) = N'SELECT * FROM Orders ORDER BY OrderID OFFSET @skip ROWS FETCH NEXT @take ROWS ONLY'; EXEC sp_executesql @sql, N'@skip INT, @take INT', @skip = 40, @take = 20;
- 不要用字符串拼接变量值,防止 SQL 注入;
@take必须是正整数,负数或 0 会导致语法错误
OFFSET FETCH 看似简单,但排序依赖、偏移起点、性能拐点和参数限制这四点,任何一个没对齐都会让分页结果出错或响应变慢。尤其是生产环境里突然出现“第 200 页加载要 8 秒”,大概率是偏移量过大又没加覆盖索引。










