OFFSET/FETCH 必须配合 ORDER BY 使用,否则报错;偏移量为 (@page-1)*@page_size,FETCH 后必须加 ONLY;深度分页性能差因顺序丢弃前 N 行,推荐键集分页替代。

OFFSET FETCH 在 SQL Server 2012+ 中是合法且推荐的分页语法,但不加 ORDER BY 就直接用,一定会报错——这不是可选项,是硬性限制。
为什么必须写 ORDER BY?
SQL Server 强制要求 OFFSET 只能出现在含 ORDER BY 的查询末尾。没写、写错顺序、或 ORDER BY 字段没出现在 SELECT 列表里(比如按计算列排序但没选出来),都会触发错误:Msg 102, Level 15, State 1, Line X Incorrect syntax near 'OFFSET'。
常见误写包括:
• 把 ORDER BY 放在 OFFSET 后面(语法不允许)
• 在视图或内联表值函数里用了 OFFSET,但调用时没补 ORDER BY
• SELECT * 但 ORDER BY 用的是未显式包含的列(如 ORDER BY created_at DESC,但表里有多个时间字段且没明确选)
OFFSET 和 FETCH NEXT 的参数怎么算?
前端传的是页码 @page 和每页条数 @page_size,SQL 层要转成偏移量和取数逻辑:
• 第 1 页 → OFFSET 0 ROWS
• 第 2 页(每页 10 条)→ OFFSET 10 ROWS
• 通用公式:OFFSET (@page - 1) * @page_size ROWS FETCH NEXT @page_size ROWS ONLY
注意:ONLY 不能省,漏写会语法报错;FETCH NEXT 后面不能跟 FOR UPDATE 或其他修饰
深度分页(比如 OFFSET 超过 5 万)为什么慢?
OFFSET 不是“跳到索引第 N 条”,而是让 SQL Server 先扫描并丢弃前 N 行——哪怕 ORDER BY 字段有索引,逻辑读也会随偏移量线性增长。
实际影响:
• OFFSET 100000 ROWS 可能导致执行计划中「实际行数」高达 10 万 + @page_size
• 响应时间从毫秒级升至秒级,尤其在高并发场景下容易拖垮连接池
• 替代方案不是禁用 OFFSET FETCH,而是改用键集分页(Keyset Pagination):用上一页最后一条记录的主键做条件,例如 WHERE id > 123456 ORDER BY id OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY
存储过程中怎么安全用 OFFSET FETCH?
别拼动态 SQL 做表名或排序字段,容易注入且难调试。
推荐做法:
• 用参数化变量控制 @page 和 @page_size,直接参与 OFFSET 计算
• ORDER BY 字段固定写死(如 ORDER BY id ASC),避免运行时传入排序字段名
• 如果真需动态排序字段,必须用 QUOTENAME() 包裹,且只允许白名单内的列名
• 检查 @page_size 是否为正整数,防止负数或零导致 FETCH NEXT 0 ROWS 报错
真正麻烦的不是语法写不对,是 OFFSET 在大数据偏移时的执行行为——它看起来像跳转,实际是顺序丢弃。上线前务必看执行计划里的「实际行数」,而不是只信「预估行数」。

















