ROWNUM分页需三层嵌套以解耦排序、编号与截断,WHERE ROWNUM>10恒为空因ROWNUM边取边判;OFFSET FETCH要求Oracle 12c+且必须显式ORDER BY。

ROWNUM 和 OFFSET FETCH 是 Oracle 中实现分页的两条主线,但它们适用场景、语法约束和性能表现差异极大。直接用错一个,轻则查不到数据,重则拖垮整库。
为什么 WHERE ROWNUM > 10 永远返回空?
因为 ROWNUM 不是“先编号再过滤”,而是“边取边编、边编边判”。第一行进来被标为 1,不满足 > 10 立刻丢弃;第二行又变成 1,继续丢弃……永远没有机会生成 11。
所以你不能写:
SELECT * FROM employees WHERE ROWNUM > 10;
必须靠子查询把编号“固化”成普通列名才能安全过滤。常见错误包括:
- 把
ORDER BY放在中间层或外层 ——ROWNUM已按原始扫描顺序分配,排序无效 - 漏掉中间层的
WHERE ROWNUM —— Oracle 会全表排序+编号,海量数据下 I/O 爆炸 - 参数算错:第 2 页(每页 10 条)对应的是
rnum > 10,不是>= 11或> 20
OFFSET FETCH 的硬性前提:Oracle 12c 及以上 + 显式 ORDER BY
OFFSET FETCH 语法简洁,但有两个强制条件:
- 数据库版本必须 ≥ 12c(含 12cR1),11g 及更早版本不识别该语法,会报
ORA-00933: SQL command not properly ended -
ORDER BY必须出现在OFFSET前,且不能省略 —— 没有排序依据的分页无业务意义,Oracle 也拒绝执行
正确写法:
SELECT * FROM employees ORDER BY employee_id OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
错误写法(即使语法通过,结果也不可靠):
SELECT * FROM employees OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; -- ❌ 缺 ORDER BY
注意:OFFSET 0 ROWS 合法,但 OFFSET 参数为负数或非整数会直接报错,不能传动态计算出的浮点值。
三层 ROWNUM 嵌套的标准结构怎么写?
这不是炫技,而是解耦「排序」「编号」「截断」三件事。每一层只干一件事:
- 最内层:只做
ORDER BY,确保逻辑顺序,不加任何ROWNUM或过滤 - 中间层:对已排序结果加
ROWNUM,并立即用WHERE ROWNUM 截断物理读取(这是性能关键) - 最外层:把中间层的
ROWNUM当普通列(如别名为rn),用WHERE rn > :min_row安全过滤起始位置
示例(查第 3 页,每页 15 条):
SELECT * FROM (
SELECT a.*, ROWNUM rn FROM (
SELECT * FROM employees ORDER BY hire_date DESC
) a WHERE ROWNUM 30;
其中 :min_row = (3-1)*15 = 30,:max_row = 3*15 = 45。若排序字段(如 hire_date)没索引,这一步会大量使用临时表空间。
什么时候该放弃 ROWNUM,改用 ROW_NUMBER()?
如果你用的是 Oracle 9i 或更高版本(基本都满足),且需要更灵活的过滤(比如 BETWEEN、> 直接写),ROW_NUMBER() OVER (ORDER BY ...) 是更干净的选择:
- 它先完成全部排序,再统一编号,因此可直接在外层用
WHERE rn BETWEEN 21 AND 30 - 支持窗口函数的其他能力(如分区、重复值处理),适合复杂报表场景
- 但注意:它仍需全排序,大数据量下性能未必优于带
WHERE ROWNUM 的 <code>ROWNUM方案
示例:
SELECT * FROM ( SELECT e.*, ROW_NUMBER() OVER (ORDER BY salary DESC) rn FROM employees e ) WHERE rn BETWEEN 21 AND 30;
真正容易被忽略的点是:当排序字段允许 NULL 时,ORDER BY salary DESC 默认把 NULL 排最前(Oracle 行为),可能让第 1 页全是 NULL 数据。应显式写成 ORDER BY salary DESC NULLS LAST。











