ROWNUM是伪列,分配在WHERE过滤前导致WHERE ROWNUM>5无结果;ROW_NUMBER()是窗口函数,先排序再编号,可直接用于分页过滤。

ROWNUM 是伪列,ROW_NUMBER() 是窗口函数;想分页又排序,直接用 ROW_NUMBER() 更可靠,别硬套 ROWNUM。
ROWNUM 为什么不能直接写 WHERE ROWNUM > 5?
因为 ROWNUM 分配发生在 WHERE 过滤之前:Oracle 拿到第一行就赋值 ROWNUM = 1,发现 ROWNUM > 5 为 false,直接丢弃;第二行又变成“第一行”,继续赋 ROWNUM = 1,无限循环。结果就是 WHERE ROWNUM > n(n > 1)永远没数据。
- 能用:
WHERE ROWNUM (取前 N 行) - 不能用:
WHERE ROWNUM BETWEEN 6 AND 10或WHERE ROWNUM > 5 - 变通写法必须套子查询:
SELECT * FROM (SELECT ..., ROWNUM r FROM (SELECT ... ORDER BY sal DESC) WHERE ROWNUM 5
ROW_NUMBER() 为什么能直接在 WHERE 里用?
它属于窗口函数,执行顺序是:先完成 ORDER BY 排序(或 PARTITION BY 分组),再给每行算编号。所以编号是稳定、可预测的,WHERE num BETWEEN 6 AND 10 能准确命中目标行。
- 排序后编号:
ROW_NUMBER() OVER (ORDER BY sal DESC) - 分组内编号:
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal DESC) - 可直接过滤:
WHERE rn BETWEEN :start AND :end,不用嵌套两层子查询
ORDER BY 和 ROWNUM 的执行顺序陷阱
很多人以为 SELECT * FROM emp ORDER BY sal DESC WHERE ROWNUM 会返回工资最高的 5 条——错。Oracle 实际执行顺序是:先取数据、分配 <code>ROWNUM、再排序。也就是说,ROWNUM 是按未排序的原始扫描顺序生成的,ORDER BY 对它没影响。
- 正确写法必须把
ORDER BY放在内层子查询:(SELECT * FROM emp ORDER BY sal DESC)先排好,再套WHERE ROWNUM - 而
ROW_NUMBER()的OVER (ORDER BY ...)明确绑定排序逻辑,不会混淆执行阶段 - 性能上,
ROW_NUMBER()多一次排序开销,但语义清晰、不易出错;ROWNUM看似轻量,但嵌套深、易漏条件
什么时候还非得用 ROWNUM?
极少数场景下,比如你只想要“物理扫描顺序的前 N 行”,不关心业务排序,且表有合适索引支持快速定位(如主键升序查前 10 条),WHERE ROWNUM 可能比 <code>ROW_NUMBER() 少一次全排序。
- 适用:
SELECT * FROM orders WHERE status = 'NEW' AND ROWNUM (假设 status 字段有索引,且不需要按时间/ID 排序) - 不适用:任何需要“按某字段倒序取中间页”的需求,比如第 21–30 条最新订单
- 注意:
ROWNUM不支持PARTITION BY,也无法做并列排名(RANK()/DENSE_RANK()那种)
真正容易被忽略的点是:ROWNUM 的行为依赖于 Oracle 查询执行路径,哪怕加了索引、改了 hint,只要扫描顺序变了,ROWNUM 值就可能不同;而 ROW_NUMBER() 的结果只取决于 OVER 子句里的逻辑,可控性高得多。


















