SQL Server 2012+ 推荐用 OFFSET ... FETCH NEXT 分页,需配合 ORDER BY,OFFSET 偏移量为 (@pageIndex-1)*@pageSize;MySQL 8.0+ 用 LIMIT offset, size,注意顺序和深分页性能;PostgreSQL 同样存在 OFFSET 性能问题,建议大偏移时抛异常并改用游标分页。

SQL Server 中用 OFFSET 和 FETCHE NEXT 实现分页最直接
SQL Server 2012+ 支持标准的 OFFSET ... FETCH NEXT 语法,写法清晰、语义明确,且执行计划通常比老式 ROW_NUMBER() 嵌套更优。但要注意:必须配合 ORDER BY,否则报错 "The OFFSET clause cannot be used without an ORDER BY clause"。
常见错误是只写 OFFSET @pageOffset ROWS 却漏掉 FETCH NEXT @pageSize ROWS ONLY,结果返回全部数据而非一页;另外,@pageOffset 应为 (@pageIndex - 1) * @pageSize,传入页码从 1 开始时别直接乘。
示例片段:
CREATE PROCEDURE GetUsersPaged
@pageIndex INT = 1,
@pageSize INT = 10
AS
BEGIN
SELECT id, name, email
FROM users
ORDER BY id ASC
OFFSET (@pageIndex - 1) * @pageSize ROWS
FETCH NEXT @pageSize ROWS ONLY;
ENDMySQL 8.0+ 用 LIMIT 配合 OFFSET 最稳妥
MySQL 的 LIMIT offset, size 是最常用写法,但注意参数顺序:先偏移量再数量,和 SQL Server 相反。容易把 LIMIT @pageSize OFFSET @offset 写成 LIMIT @offset, @pageSize 混淆——后者才是正确形式。
性能隐患在深分页:当 @offset 很大(比如第 10000 页),MySQL 仍需扫描前 N 行。线上高并发场景建议改用「游标分页」(基于上一页最后 ID 查询),但存储过程里实现游标需额外参数和逻辑判断,不是所有业务都适用。
关键点:
-
@offset必须是非负整数,传负值会报错"You have an error in your SQL syntax" - 若用预编译语句(如 JDBC),确保
SET @offset = ?后再拼接,避免 SQL 注入 - 不推荐在存储过程中拼接动态 SQL 来处理可变排序字段,易出错且难维护
PostgreSQL 分页要防 OFFSET 性能退化
PostgreSQL 同样支持 OFFSET + LIMIT,但和 MySQL 一样,大偏移量会导致全表扫描。它还支持更高效的 cursor-based pagination,但原生存储过程(PL/pgSQL)中无法直接返回游标给客户端,通常得靠应用层配合实现。
如果坚持用存储过程返回分页结果,建议加一层检查:
- 当
@offset > 10000时,抛出自定义异常RAISE EXCEPTION 'Offset too large, use cursor-based pagination instead' - 排序字段必须有索引,否则
ORDER BY本身就成了瓶颈 - 避免在
WHERE子句中对分页字段做函数操作(如WHERE UPPER(name) LIKE '%X%'),会导致索引失效
跨数据库兼容性问题不能靠存储过程硬扛
没有一种分页语法能在 SQL Server、MySQL、PostgreSQL 间完全通用。试图用同一个存储过程适配多个数据库,最终往往变成一堆 IF @@VERSION LIKE '%Microsoft%' 或 IF @@sql_mode LIKE '%STRICT%' 分支,可读性和维护性极差。
真实项目中更可行的做法是:
- 在 ORM 层(如 Entity Framework、MyBatis、Django ORM)统一处理分页,由框架生成对应方言
- 若必须用存储过程,按数据库单独编写,命名带上后缀,如
GetOrdersPaged_SQLServer、GetOrdersPaged_MySQL - 不要在存储过程中计算总页数(
COUNT(*))和数据一起返回——这会强制两次扫描。应拆成两个独立调用,或让应用层自行缓存总数
分页看似简单,但排序稳定性、NULL 值排序行为、时间字段精度导致的重复记录、以及事务隔离级别对分页结果的影响,都是上线后才暴露的点。别只测第 1 页和最后 1 页,一定要压测中间页和边界偏移量。

















