ROWNUM只能用于1)、BETWEEN等操作,因其分配机制要求必须从1开始连续生成,不满足条件时计数器不递增,导致后续行始终无法获得有效序号。

为什么直接用 ROWNUM > N 会查不到数据
因为 ROWNUM 是在结果集生成过程中动态分配的,且**从 1 开始、逐行递增**。Oracle 先取第 1 行,赋 ROWNUM = 1,再判断 WHERE 条件;如果条件是 ROWNUM > 5,这一行立刻被丢弃。接着取第 2 行,又赋 ROWNUM = 1(因为上一行没通过,计数器不进位),继续失败……如此循环,永远没有满足条件的行。
所以这些写法都无效:
SELECT * FROM emp WHERE ROWNUM > 5SELECT * FROM emp WHERE ROWNUM >= 10SELECT * FROM emp WHERE ROWNUM BETWEEN 6 AND 10
根本原因:ROWNUM 只能用 或 = 1 在最外层或单层子查询中直接过滤,否则逻辑不通。
三层嵌套是标准写法,但必须按顺序执行
正确分页依赖三步不可颠倒的嵌套:
- 最内层:完成排序和业务过滤(如
ORDER BY salary DESC),确保逻辑顺序正确 - 中间层:加
ROWNUM别名(如rn),并用ROWNUM 截断——这是唯一允许用的地方 - 最外层:用别名
rn做范围筛选,比如rn > (page-1)*size
例如查第 2 页、每页 5 条(即第 6–10 行):
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM employees ORDER BY hire_date DESC
) a WHERE ROWNUM 5;
漏掉中间层的 WHERE ROWNUM ,最外层 rn > 5 就可能扫描全表,性能崩坏。
ROWNUM 分页必须配合 ORDER BY,且排序必须在最内层
很多人忽略这点:如果排序放在最外层或中间层,ROWNUM 会先按未排序的物理读取顺序编号,导致“第 6–10 行”不是你想要的最新/最高/指定顺序的数据。
错误示范(排序无效):
SELECT * FROM ( SELECT e.*, ROWNUM rn FROM employees e ) WHERE rn BETWEEN 6 AND 10 ORDER BY salary DESC;
正确结构必须是:
- 内层子查询先
ORDER BY,生成确定顺序的结果集 - 中间层对这个有序结果集打
ROWNUM - 外层只做行号切片,不再动顺序
否则分页结果每次执行都可能不同,尤其在并发写入频繁的表上。
Oracle 12c+ 推荐改用 OFFSET/FETCH,但要注意兼容性
如果你用的是 Oracle 12c 或更高版本,直接用标准 SQL 更安全清晰:
SELECT * FROM employees ORDER BY hire_date DESC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;
它天然支持排序 + 跳过 + 限制,无需嵌套,语义明确,也避免了 ROWNUM 的所有陷阱。
但注意:
- 低于 12c 的版本不支持,会报
ORA-00933: SQL command not properly ended - 如果应用需兼容老版本 Oracle,不能无脑替换
-
OFFSET值很大时(如跳过 100 万行),性能仍可能下降,因 Oracle 仍需扫描前 N 行
真正容易被忽略的点是:即使用了 OFFSET/FETCH,ORDER BY 依然不可省——没有排序,分页结果无意义,且 Oracle 会强制要求它存在。











