ROWNUM是Oracle数据库中用于为查询结果集每一行动态分配从1开始递增的伪列,常用于限制返回行数、分页及Top-N查询;其值在WHERE过滤前生成,故不能直接使用>、>=等条件(除ROWNUM=1外),需嵌套子查询配合ORDER BY实现有序分页。

ROWNUM
在 Oracle 中,ROWNUM 只能可靠用于「小于等于」条件,比如 WHERE ROWNUM 。这是因为 <code>ROWNUM 在 Oracle 执行查询时是边取数据、边编号、边过滤的:第一行进来被标为 1,满足条件就保留;第二行进来标为 2,再判断……直到不满足 ROWNUM 就停止。一旦写成 <code>ROWNUM > 5 或 ROWNUM = 2,Oracle 在第一行(ROWNUM = 1)时就不满足条件,直接跳过,后续行根本不会被编号,结果永远为空。
常见错误现象:
-
SELECT * FROM emp WHERE ROWNUM = 2→ 返回空集 -
SELECT * FROM emp WHERE ROWNUM > 10→ 返回空集 SELECT * FROM emp ORDER BY sal DESC WHERE ROWNUM → 实际返回的是「未排序前的前 5 行再排序」,不是薪资最高的 5 人
要取「第 M 到第 N 行」必须三层嵌套
分页或取中间段数据(如第 11–20 行)不能靠单层 ROWNUM,必须用三层结构:最内层排序 → 中间层加 ROWNUM 并截断上限 → 外层用别名过滤下限。关键点是:排序必须在最内层完成,否则 ROWNUM 分配顺序和业务顺序不一致。
实操建议:
- 最内层子查询必须带
ORDER BY,否则分页结果不可预测 - 中间层必须给
ROWNUM起别名(如rnum),否则外层无法引用 - 中间层的
WHERE ROWNUM 是性能关键,它触发 Oracle 的 Top-N 优化,只维护最多 <code>:end行的内存排序缓冲区 - 外层用
WHERE rnum >= :start,不能写成BETWEEN,因为BETWEEN在 Oracle 旧版本中可能绕过 Top-N 优化
示例(取按 id 升序的第 11–20 行):
SELECT * FROM (
SELECT rnum, id, name FROM (
SELECT id, name FROM users ORDER BY id
) WHERE ROWNUM <= 20
) WHERE rnum >= 11;ROWNUM 和 ROW_NUMBER() 不可混用
ROWNUM 是伪列,生成时机早于 ORDER BY 和大部分逻辑处理;而 ROW_NUMBER() 是窗口函数,在完整结果集生成后才计算,支持分区、灵活排序、任意比较。二者语法和语义完全不同,不能互相替代或拼接使用。
容易踩的坑:
- 在同一个查询里同时写
ROWNUM和ROW_NUMBER() OVER (ORDER BY ...)→ 结果行为不可控,Oracle 不保证执行顺序 - 用
ROW_NUMBER()替代ROWNUM做简单 Top-N(如前 10 条)→ 失去 Oracle 内置的 Top-N 优化,全表排序开销大 - 误以为
ROWNUM = ROW_NUMBER() OVER (ORDER BY ...)→ 仅当原始数据已按该字段物理有序且无WHERE过滤时才偶然相等
Oracle 19c 中 ROWNUM 的实际性能影响
在 19c 中,ROWNUM 仍会触发 Top-N 优化,但效果依赖执行计划是否真正走「WINDOW NOSORT STOPKEY」。如果内层排序字段无索引,或存在复杂 <code>JOIN/FILTER,优化可能失效,退化为全结果集排序后截断。
提升稳定性的做法:
- 确保
ORDER BY字段有合适索引(升序/降序需匹配) - 避免在最内层子查询中使用函数表达式排序(如
ORDER BY UPPER(name)),否则索引失效 - 用
EXPLAIN PLAN检查执行计划中是否出现STOPKEY关键字 - 对超大数据集分页,考虑用
OFFSET ... FETCH NEXT(12c+ 支持),语义更清晰,且 19c 对其优化更成熟
真正难处理的从来不是怎么写,而是搞清「哪一层排序生效」「哪一层 ROWNUM 被分配」「优化器有没有真正省掉多余排序」——这三者错一个,分页就慢或错。


















