oracle 12c+分页必须强制order by,因fetch first依赖确定性排序;无order by时结果顺序不稳,易致重复、漏行或行数波动,且性能随offset增大急剧下降。

Oracle 12c 及以上版本必须用 ORDER BY + OFFSET + FETCH FIRST 三者共存,缺一不可;否则分页结果不可靠,不是语法错误,而是逻辑错乱。
为什么 ORDER BY 不是可选而是强制条件
Oracle 的 FETCH FIRST 不像 MySQL 的 LIMIT 那样“先排序后切片”,它依赖排序结果的确定性。没有 ORDER BY 时,Oracle 不报错,但每次执行可能返回不同顺序的行——尤其是并发查询或表有并行扫描时。更隐蔽的问题是:同一页面刷新两次,OFFSET 20 FETCH FIRST 10 ROWS ONLY 可能返回 8 行、10 行甚至重复行。
-
ORDER BY字段必须有索引,否则每次分页都触发全表扫描 - 如果排序字段存在大量重复值(如
status),必须追加唯一列(如id)构成复合排序:ORDER BY status DESC, id ASC - 避免在
ORDER BY中使用函数(如ORDER BY UPPER(name)),否则索引失效,性能断崖式下降
OFFSET 越大,性能越差,这不是 Oracle 特有,而是 SQL 执行模型决定的
OFFSET 100000 意味着 Oracle 必须扫描并丢弃前 100000 行,哪怕你只取 20 行。实测在亿级表中,OFFSET 500000 查询耗时可达 3 秒以上,且 CPU 和 I/O 压力陡增。
- 不要把“页码 × 每页条数”直接代入
OFFSET参数,尤其在后台管理类接口(如页码 > 500) - 深分页场景(> 1000 行偏移)应切换为键集分页(keyset pagination),例如记录上一页最后的
(created_at, id),下一页用WHERE (created_at, id) -
FETCH FIRST 20 ROWS ONLY和FETCH NEXT 20 ROWS ONLY功能完全等价,选哪个纯看团队习惯,但必须和ORDER BY同时出现
WITH TIES 是解决“并列排序值导致漏行”的唯一标准方案
当多行共享相同排序键(如 score = 95),FETCH FIRST 5 ROWS ONLY 可能只取其中几行,导致第 1 页末尾和第 2 页开头出现同一 score 的不同行——用户感知就是“数据跳变”或“重复出现”。
- 用
FETCH FIRST 5 ROWS WITH TIES可强制包含所有并列行,返回行数 ≥ 5 -
WITH TIES仅依据ORDER BY的完整表达式判定并列,例如ORDER BY score DESC, id ASC中,id仅用于打破并列,不参与WITH TIES判定 -
WITH TIES在 Oracle 12.1+ 支持,但部分早期 12c 补丁包存在 bug,建议生产环境使用 12.2 或更高版本
真正难的不是写出语法正确的语句,而是判断什么时候该放弃 OFFSET、什么时候必须加 WITH TIES、以及如何让 ORDER BY 同时满足业务语义和索引效率——这些细节一旦疏忽,线上分页接口就容易在流量高峰突然变慢或返回错乱数据。











