Oracle数据库的分页查询是业务开发中高频使用的功能,尤其是在列表展示类场景中,合理选择分页方案能大幅提升查询性能,避免不必要的资源消耗。

Oracle分页的核心原理
Oracle本身没有像MySQL那样直接的LIMIT分页语法,最常用的分页方式是借助ROWNUM伪列实现。ROWNUM是Oracle为查询结果集动态分配的序号,从1开始递增,只有在数据被筛选出来之后才会分配序号,这也是很多开发者写分页SQL出错的核心原因。
需要注意的是,ROWNUM不支持大于号判断,比如ROWNUM > 5这样的条件永远无法成立,因为第一条数据的ROWNUM是1,不满足大于5的条件就会被过滤,后续数据的ROWNUM也不会再递增。
常用分页方案及示例
方案一:基于ROWNUM的基础分页
这是最常用的分页方式,适合大多数中小数据量的场景,核心思路是先对数据进行排序,再用子查询包装后筛选ROWNUM范围。
-- 查询第11到20条数据,每页10条,按id升序排序
SELECT *
FROM (
SELECT t.*, ROWNUM rn
FROM (
-- 最内层先写原始查询逻辑,包含排序
SELECT id, name, create_time
FROM user_info
WHERE status = 1
ORDER BY id ASC
) t
WHERE ROWNUM <= 20 -- 先筛选小于等于最大页码的行
)
WHERE rn >= 11 -- 再筛选大于等于最小页码的行
方案二:12c及以上版本的FETCH分页
Oracle 12c之后引入了类似SQL标准的FETCH分页语法,写法更简洁,可读性更强,适合新版本Oracle数据库使用。
-- 同样查询第11到20条数据,按id升序排序 SELECT id, name, create_time FROM user_info WHERE status = 1 ORDER BY id ASC OFFSET 10 ROWS -- 跳过前10条数据 FETCH NEXT 10 ROWS ONLY; -- 取接下来的10条数据
分页性能问题及优化方案
常见问题:深分页查询慢
当查询的页码很深时,比如查询第10000页的数据,基础ROWNUM分页的查询速度会明显下降,因为Oracle需要先遍历前10000*10条数据,再筛选出目标范围的数据,大量无效遍历会消耗很多资源。
优化方法
- 建立合适的索引:最内层查询的排序字段和筛选条件要匹配索引,比如按id排序就给id建立索引,避免全表排序。
- 使用ROWID优化深分页:先通过索引查询到目标页的ROWID,再通过ROWID回表查询数据,减少回表次数。
以下是ROWID优化深分页的示例:
-- 先通过索引查询目标页的ROWID,假设id是主键有索引
SELECT *
FROM user_info
WHERE ROWID IN (
SELECT rid
FROM (
SELECT rid, ROWNUM rn
FROM (
SELECT ROWID rid
FROM user_info
WHERE status = 1
ORDER BY id ASC
)
WHERE ROWNUM <= 100020 -- 最大页码对应的行数
)
WHERE rn >= 100011 -- 最小页码对应的行数
)
ORDER BY id ASC;
注意事项
分页查询时,最内层的排序必须保证唯一性,如果排序字段存在重复值,可能会导致不同页的数据出现重复或者遗漏,建议排序字段搭配主键一起排序,比如ORDER BY create_time DESC, id ASC。不同方案的选择建议
| 分页方案 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| ROWNUM基础分页 | Oracle所有版本,中小数据量分页 | 兼容性好,所有版本都支持 | 深分页性能差,写法相对复杂 |
| FETCH分页 | Oracle 12c及以上版本 | 写法简洁,可读性强 | 低版本Oracle不支持 |
| ROWID优化分页 | 千万级大表深分页场景 | 深分页性能大幅提升 | 写法复杂,需要排序字段有索引 |