rownum不能直接与order by混用,因为rownum在排序前分配,导致先取前n行再排序,而非对全部数据排序后取前n行;正确做法是用子查询先order by排序,外层再用rownum限制行数。

ROWNUM 不能直接和 ORDER BY 混用
直接写 SELECT * FROM emp WHERE ROWNUM ,结果是错的:Oracle 先按原始顺序给前 10 行打上 ROWNUM=1~10,再排序,最终看到的只是这 10 行乱序重排后的样子,不是“工资最高的前 10 人”。
必须用子查询先排序,再套一层 ROWNUM 过滤:
SELECT * FROM ( SELECT * FROM emp ORDER BY salary DESC ) WHERE ROWNUM
- ROWNUM 是在结果集生成过程中动态分配的,分配完就固定了,不随后续排序改变
- 子查询外层不能再加 ORDER BY(除非再套一层),否则可能破坏 ROWNUM 逻辑
- 如果要取第 11–20 行,不能写
WHERE ROWNUM > 10 AND ROWNUM ——这永远返回空,因为 ROWNUM 总是从 1 开始分配,第一条就不满足 <code>> 10
FETCH FIRST N ROWS ONLY 更安全、更直观
Oracle 12c 及以后版本推荐用 FETCH FIRST,它天然支持排序后截断,语义清晰,不易出错。
例如获取入职最早的 5 名员工:
SELECT * FROM emp ORDER BY hire_date ASC FETCH FIRST 5 ROWS ONLY;
- 不需要嵌套子查询,ORDER BY 和 FETCH 在同一层级生效
- 支持偏移分页:
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY直接跳过前 20 行取 10 条 - 注意:
FETCH FIRST 0 ROWS ONLY合法,但FETCH FIRST -1 ROWS ONLY会报错ORA-00933: SQL command not properly ended - 老版本 Oracle(
ROWNUM 分页时容易漏数据或重复
手动实现“第 2 页(每页 10 条)”时,常见错误写法:WHERE ROWNUM BETWEEN 11 AND 20 或 ROWNUM > 10 AND ROWNUM ,这两者都查不到任何数据。
正确做法是两层嵌套:
SELECT * FROM (
SELECT a.*, ROWNUM rnum FROM (
SELECT * FROM emp ORDER BY salary DESC
) a WHERE ROWNUM 10;
- 内层子查询控制上限(
ROWNUM ),避免外层 <code>rnum > 10时已无数据可筛 - 中间别名
rnum是普通列,可以正常比较;而外层ROWNUM伪列不可用于>判断 - 若原表有大量数据且排序字段无索引,这种嵌套会导致全表扫描+大排序,性能明显劣于
FETCH的执行计划
ROWNUM 和 FETCH 的底层行为差异
两者看似效果相似,但执行机制不同:ROWNUM 是逐行判断是否纳入结果集,而 FETCH 是先完成整个排序,再按位置切片。
- 对含
ROWNUM的查询,Oracle 可能提前终止排序(如发现已凑够 10 行就停),但实际优化器行为依赖统计信息和版本,不可强依赖 -
FETCH FIRST明确要求完整排序后再截断,语义确定,适合需要严格 Top-N 结果的场景(如报表取最高薪员工) - 如果查询中用了
UNION ALL或复杂视图,FETCH的解析和权限检查更严格,某些情况下会报ORA-30561: FETCH clause not allowed in this context
用错 ROWNUM 的地方,往往不是语法写错,而是没意识到它和排序的执行时序冲突。FETCH 虽好,但别忘了确认数据库版本——很多生产库还在 11g,得老老实实用子查询套 ROWNUM。











