Oracle 12c+ 应优先使用 OFFSET ... FETCH FIRST 分页,因其严格按 ORDER BY→OFFSET→FETCH 执行,语义清晰、结果可控;而 ROWNUM 需两层子查询且在排序前赋值,WHERE ROWNUM BETWEEN 直接无效。

ROWNUM 不是排序后取前 N 条的可靠方式,必须嵌套子查询加 ORDER BY 才能保证结果稳定。
用 ROWNUM 取前 N 条数据,为什么直接写会错?
Oracle 的 ROWNUM 是在结果集生成过程中动态分配的,它在 ORDER BY 之前就已产生。所以 SELECT * FROM emp WHERE ROWNUM 实际上是先取任意 10 行,再对这 10 行排序——完全不是“薪资最高的前 10 人”。
- 正确写法必须两层嵌套:
SELECT * FROM (SELECT * FROM emp ORDER BY sal DESC) WHERE ROWNUM <= 10
- 如果还要跳过前 M 条(比如分页查第 21–30 条),不能用
ROWNUM > 20,因为ROWNUM永远从 1 开始;得用外层再套一层并用别名过滤:SELECT * FROM (SELECT ROWNUM r, t.* FROM (SELECT * FROM emp ORDER BY sal DESC) t) WHERE r > 20 AND r <= 30
- 注意:全表扫描不可避免,大数据量时性能差;Oracle 12c+ 推荐改用
OFFSET ... FETCH NEXT
DECODE 和 CASE WHEN 哪个该用?
DECODE 是 Oracle 专有函数,仅支持等值判断,语法紧凑但可读性弱;CASE WHEN 是 SQL 标准语法,支持任意布尔表达式,嵌套和扩展更自然。
- 简单映射用
DECODE没问题:DECODE(dept_id, 10, 'IT', 20, 'HR', 'OTHER') - 涉及范围、NULL 判断或复杂条件,必须用
CASE WHEN:CASE WHEN sal > 10000 THEN 'HIGH' WHEN sal IS NULL THEN 'MISSING' ELSE 'NORMAL' END -
DECODE对 NULL 的处理是特殊逻辑(DECODE(x, NULL, ...)永远不匹配),而CASE中WHEN x IS NULL明确可控
字符串拼接用 || 还是 CONCAT?
Oracle 中 || 是首选,CONCAT 功能受限且易出错。
-
CONCAT最多只接受两个参数,CONCAT(a, CONCAT(b, c))冗余又难读;a || b || c直观清晰 -
CONCAT遇到任一参数为NULL时返回NULL,而||会把NULL当空字符串处理(更符合日常预期) - 若字段可能为空且需统一转为空字符串,建议配合
NVL:NVL(name, '') || ' - ' || NVL(job, '')
分页查询在 Oracle 12c+ 应该用什么?
放弃 ROWNUM 嵌套,直接用标准 OFFSET ... FETCH,语义清晰、数据库可优化、支持绑定变量安全分页。
- 取前 10 条:
SELECT * FROM emp ORDER BY sal DESC FETCH FIRST 10 ROWS ONLY
- 跳过前 20 条,取接下来 10 条:
SELECT * FROM emp ORDER BY sal DESC OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY
- 注意:
ORDER BY必须存在,否则报错;OFFSET不能为负数或非整数;老版本(11g 及以前)不支持,仍需ROWNUM套娃
实际写分页逻辑时,最容易被忽略的是 ORDER BY 字段的重复值问题——如果 sal 有大量相同值,OFFSET/FETCH 或 ROWNUM 套娃都可能导致同一页数据来回跳动。解决办法是追加唯一字段(如主键)作为第二排序项:ORDER BY sal DESC, emp_id。

















