Oracle 12c+优先用FETCH FIRST语法,必须与ORDER BY同级使用,不可嵌套;支持FETCH FIRST N ROWS ONLY、OFFSET M ROWS FETCH NEXT N ROWS ONLY及WITH TIES等用法,缺ORDER BY则结果不确定。

Oracle 12c 及以后版本,优先用 FETCH FIRST;老版本(如 11g)只能靠 ROWNUM 嵌套子查询,硬绕。
FETCH FIRST 在 Oracle 12c+ 中怎么写才不报错
它必须和 ORDER BY 在同一级 SELECT 中出现,不能丢掉排序,也不能塞进子查询里再加 FETCH。
-
FETCH FIRST 5 ROWS ONLY合法,FETCH FIRST 0 ROWS ONLY也合法(返回空集),但FETCH FIRST -1 ROWS ONLY会直接报ORA-00933 - 分页场景写
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY,注意NEXT不可省略,FETCH FIRST 10 ROWS ONLY和FETCH NEXT 10 ROWS ONLY效果一样,但语义上后者更贴合分页上下文 - 如果漏了
ORDER BY,语句能执行,但结果不可预测——Oracle 不保证无序时的“前 N 行”是哪几行 - 性能上,
FETCH会先完成全量排序再切片,若排序字段没索引,大表可能慢;但执行计划通常比 ROWNUM 嵌套更优,优化器更容易做剪枝
ROWNUM 为什么不能直接写 WHERE ROWNUM > 10
因为 ROWNUM 是动态分配的:Oracle 每生成一行就立刻编号,从 1 开始,且只增不减。所以 ROWNUM > 10 永远不成立——第一行编号是 1,不满足;第二行编号是 2,还是不满足……整条 WHERE 条件始终为假。
- 想取第 11–20 行,必须两层嵌套:
SELECT * FROM (SELECT a.*, ROWNUM rnum FROM (SELECT * FROM emp ORDER BY salary DESC) a WHERE ROWNUM 10 - 内层
WHERE ROWNUM 是为了控制上限,避免外层 <code>rnum > 10时数据已耗尽 - 中间别名
rnum是普通列,可以比较;而外层伪列ROWNUM无法用于>或=(除了=1) - 常见错误:
WHERE ROWNUM BETWEEN 11 AND 20或ROWNUM > 10 AND ROWNUM < 20,全部返回空
ORDER BY 和 ROWNUM 混用时为什么结果不对
根本原因是 ROWNUM 分配发生在排序之前。你写 SELECT * FROM emp WHERE ROWNUM ,Oracle 先随机取 10 行、打上 ROWNUM=1~10,再对这 10 行排序——不是“工资最高的前 10 人”,而是“随便 10 个人里工资排前几的”。
- 正确做法永远是子查询先
ORDER BY,外层再用ROWNUM截断:SELECT * FROM (SELECT * FROM emp ORDER BY salary DESC) WHERE ROWNUM - 外层不能再加
ORDER BY,否则可能破坏ROWNUM的逻辑顺序(尤其在并行查询或复杂视图中) - 如果原表数据量大、排序字段无索引,这种嵌套会导致全表扫描 + 大排序,内存/临时表空间压力明显高于
FETCH
什么时候必须用 ROWNUM 而不能用 FETCH
只有两种情况:Oracle 版本低于 12c,或者你正在写兼容多版本的脚本且无法判断目标库版本。
-
FETCH在 11g 及更早版本会直接报ORA-00933,没有任何回退机制 - 某些旧系统 DBA 禁用了 12c+ 新语法,或应用框架 SQL 解析器不识别
FETCH关键字 - 注意:即使版本支持,
FETCH也不支持“按部门分别取前 3 名”这类需求——这时得用DENSE_RANK()或ROW_NUMBER()等分析函数,ROWNUM同样不行
真正容易被忽略的是执行计划差异:ROWNUM 嵌套看起来只是多一层括号,但 Oracle 实际执行时很可能物化中间结果集;而 FETCH 更倾向流式处理+提前终止,尤其在有合适索引时。调优时别只看 SQL 写法,一定看 EXPLAIN PLAN 里的 VIEW 和 WINDOW SORT 节点。


















