rownum分页漏数据或错位,因rownum在where前分配且依赖物理顺序;须用两层嵌套子查询(内层排序+rownum,外层过滤),或oracle 12c+改用fetch next语法。
rownum 分页为什么总漏数据或错位
用 rownum 做分页时,常见结果比预期少一行、第二页首条重复、跳过某条记录——根本原因是 rownum 在 where 过滤前就已分配,且只对结果集的物理生成顺序生效。它不理解“排序后取第11–20行”这种逻辑。
实操建议:
- 必须把排序 +
ROWNUM限制写在内层子查询里,外层再过滤ROWNUM范围,否则 ORDER BY 会被忽略或错位 - 不要写
WHERE ROWNUM BETWEEN 11 AND 20——ROWNUM从 1 开始连续赋值,不可能有 11 - Oracle 12c 以前只能靠两层嵌套:内层排好序并打上
ROWNUM,外层查rn BETWEEN ? AND ?
示例(安全写法):
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM orders ORDER BY order_date DESC
) a WHERE ROWNUM <= 20
) WHERE rn > 10FETCH NEXT 是不是能直接替代 ROWNUM
可以,但仅限 Oracle 12c 及以上。FETCH NEXT 是 SQL:2008 标准语法,语义清晰、写法简洁,且天然支持排序后分页,不用手动管理 ROWNUM 序号。
实操建议:
-
OFFSET必须配合ORDER BY,否则报错ORA-00933: SQL command not properly ended - 避免大偏移量(如
OFFSET 1000000),性能会断崖式下降——Oracle 仍需扫描前 100 万行 - 若要支持动态页码,参数化时注意
OFFSET和FETCH NEXT都只接受整数字面量或绑定变量(12.1+ 支持绑定变量)
示例(等价于上面的 11–20 行):
SELECT * FROM orders ORDER BY order_date DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
两种写法在索引和执行计划上的差异
ROWNUM 嵌套写法容易让优化器误判,尤其外层 WHERE rn > 10 可能导致索引失效;而 FETCH NEXT 在执行计划中明确体现为 “WINDOW SORT PUSHED RANK”,Oracle 能更好利用排序字段上的索引。
实操建议:
- 确保
ORDER BY字段有索引,否则两种方式都会全表扫描 + 大内存排序 - 用
EXPLAIN PLAN对比:查ROWNUM嵌套时注意看是否出现FILTER操作在最外层,那是性能隐患信号 - 如果分页深度固定(比如只查前 100 页),
FETCH NEXT的执行计划更稳定;若要做“无限滚动”且偏移极大,考虑游标分页(WHERE last_seen_id )而非 <code>OFFSET
升级到 FETCH NEXT 后遇到 ORA-00933 怎么办
不是语法写错,大概率是数据库版本低于 12.1,或者连接时用了旧客户端/驱动未启用新特性。Oracle 12.1 才开始支持 OFFSET/FETCH,11g 及更早版本直接报错。
实操建议:
- 先确认版本:
SELECT * FROM v$version;,看到Oracle Database 12c且小版本 ≥ 1 才可用 - 检查 JDBC 驱动:ojdbc7 支持有限,ojdbc8 是必须的;Spring Boot 项目需确认
spring.jpa.database-platform设为org.hibernate.dialect.OracleDialect或更高版本 - PL/SQL 匿名块里不能直接用
FETCH NEXT当作查询语句赋值给REF CURSOR,需用动态 SQL(OPEN cur FOR sql_str)
容易被忽略的一点:即使数据库是 12c,如果初始化参数 optimizer_features_enable 被设成 11.2.0,也可能禁用部分新语法解析——别只查版本,要看实际运行时行为。










