ROWNUM不能直接写WHERE ROWNUM >= 5,因其在结果集生成时动态从1开始分配,不满足条件的行被丢弃后下一行仍从1重新计数,导致永远无法返回第5行及以后的数据。

ROWNUM 为什么不能直接写 WHERE ROWNUM >= 5
因为 ROWNUM 是在结果集生成过程中动态分配的,且**永远从 1 开始**。Oracle 每拿到一行就立刻赋值 ROWNUM = 1,再判断是否满足 WHERE 条件;如果不满足(比如 ROWNUM >= 5),这一行就被丢弃,下一行进来时又重新从 ROWNUM = 1 开始计数。所以永远没有“第 5 行”能被保留下来。
常见错误现象:
-
SELECT * FROM emp WHERE ROWNUM > 5→ 返回空结果 -
SELECT * FROM emp WHERE ROWNUM BETWEEN 5 AND 10→ 返回空结果 -
SELECT ROWNUM, ename FROM emp ORDER BY sal DESC→ROWNUM顺序和ORDER BY不一致(因为排序发生在ROWNUM分配之后)
正确分页:必须用两层子查询套住 ROWNUM
核心思路是:先在内层子查询中完成排序,并把 ROWNUM 固定为一个普通别名(如 rn);外层再对这个别名做范围筛选。这样 rn 就是静态的、可比较的数值了。
例如,查第 21–30 行(每页 10 条,第 3 页):
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM emp ORDER BY sal DESC
) a WHERE ROWNUM <= 30
) WHERE rn >= 21;关键点:
- 最内层负责
ORDER BY,确保逻辑顺序正确 - 中间层用
WHERE ROWNUM 截断,避免外层扫描全表 - 外层用别名
rn做>= 21,这是唯一安全的写法 - 如果漏掉中间层的
WHERE ROWNUM ,性能会急剧下降(全表排序后才截断)
ORDER BY 必须放在最内层子查询里
ROWNUM 的分配发生在排序之前——如果你把 ORDER BY 放在外层或中间层,ROWNUM 会基于未排序的结果分配,导致分页错乱。
错误写法(ROWNUM 基于无序数据生成):
SELECT * FROM ( SELECT ROWNUM rn, t.* FROM emp t ) WHERE rn BETWEEN 21 AND 30 ORDER BY sal DESC;
正确写法(排序先行,再编号):
SELECT * FROM (
SELECT ROWNUM rn, t.* FROM (
SELECT * FROM emp ORDER BY sal DESC
) t WHERE ROWNUM <= 30
) WHERE rn >= 21;注意:ORDER BY 不能出现在最外层用于分页逻辑,否则无法保证前 N 行就是你想要的 Top N。
Oracle 12c+ 可用 FETCH FIRST 替代 ROWNUM
如果你用的是 Oracle 12c 或更高版本,FETCH FIRST 语法更直观、不易出错,也支持真正的偏移分页:
SELECT * FROM emp ORDER BY sal DESC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
优势:
- 语义清晰:
OFFSET 20明确跳过前 20 行 - 无需嵌套子查询,可读性和维护性高
- 自动处理排序与行号绑定,不会出现
ROWNUM错位问题 - 但注意:
OFFSET值大时(如百万级偏移),性能仍可能变差,因需扫描并跳过大量行
兼容性提醒:低于 12c 的环境必须坚持两层子查询 + ROWNUM 别名方案,且务必验证排序位置和截断条件是否同时存在——这是最容易被忽略、却直接导致分页结果错乱或性能崩盘的地方。


















