<p>OFFSET 单独使用不能限制返回行数,必须配合 ORDER BY 和 FETCH 才能实现分页;仅用 OFFSET 会跳过指定行后返回全部剩余数据,且偏移量需按 (pageNo - 1) * pageSize 计算,深度分页性能急剧下降。</p>

OFFSET 在 SQL Server 2019 中不能单独“限制首行后的结果”,它必须和 ORDER BY 配合使用,且必须搭配 FETCH 才能真正截断返回行数;只写 OFFSET 不写 FETCH 会返回跳过前 N 行之后的全部剩余数据——这不是分页,而是“砍头式丢弃”。
为什么 OFFSET 单独用等于白写?
SQL Server 要求 OFFSET 必须与 ORDER BY 同时出现,但它本身不控制返回多少行。比如:
SELECT id, name FROM users ORDER BY id OFFSET 1 ROWS;
这条语句合法,但结果是:跳过第 1 行,然后把剩下所有行全吐出来(可能是几万条)。它不是“只显示第 2 行”,而是“从第 2 行开始无限制输出”。
-
OFFSET只负责“跳过”,不负责“止步” - 没加
FETCH就相当于只剪掉了开头,后面整条数据流照常倾泻 - 很多新手误以为
OFFSET 1就是“取第 2 条”,实际根本不是
正确写法:OFFSET + FETCH 是原子组合
要在首行之后只取特定数量的行(比如只取第 2 条),必须显式加上 FETCH NEXT 1 ROWS ONLY:
SELECT id, name FROM users ORDER BY id OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY;
这才是真正意义的“跳过第 1 行,只取接下来的 1 行”。注意几个硬性要求:
-
ORDER BY不可省略——没有排序,OFFSET的“第几行”完全不可靠 -
FETCH必须带ONLY关键字,漏写会报错 -
ROWS和ROW可互换,但推荐统一用复数ROWS(SQL Server 官方文档示例均用此形式) -
OFFSET 0 ROWS是合法的,表示“不跳过”,常用于第一页分页
分页参数计算容易错:(pageNo - 1) * pageSize
写分页逻辑时,OFFSET 值不是页码,而是偏移量。常见错误写法:
-- ❌ 错:把页码直接当 offset SELECT * FROM orders ORDER BY created_at DESC OFFSET 2 ROWS FETCH NEXT 10 ROWS ONLY; -- 这是第 3 页,不是第 2 页
正确映射关系是:
- 第 1 页 →
OFFSET 0 ROWS - 第 2 页 →
OFFSET 10 ROWS(假设每页 10 条) - 第 3 页 →
OFFSET 20 ROWS - 通用公式:
OFFSET (pageNo - 1) * pageSize ROWS
如果用变量传参,务必确保 (pageNo - 1) * pageSize 是非负整数,否则 SQL Server 报错:Msg 10758, Level 15, State 1: The offset value must be a non-negative integer.
性能陷阱:OFFSET 越大越慢,不是线性增长
OFFSET 1000000 ROWS 不代表数据库“快进到第 100 万零 1 行”,而是强制扫描并计数前 100 万行——哪怕你只想要 1 条。实测中:
- 表有索引且
ORDER BY列覆盖时,OFFSET 10000可能仍毫秒级 - 但
OFFSET 1000000在千万级表上可能耗时数秒甚至超时 - 此时应考虑游标分页(
WHERE id > @last_id)、键集分页,或物化中间结果 -
TOP+ 子查询在某些场景下比OFFSET/FETCH更快,但无法跳页(只能顺序翻)
最常被忽略的一点:OFFSET/FETCH 看似标准,但在深度分页场景下,它本质上是个“伪优化”——语法干净,执行代价却藏得深。上线前一定要压测真实页码,别只测第 1 页。

















