直接查大表不加限制会触发全表扫描,消耗大量io和内存,导致卡顿或超时;必须用索引+rownum分页+避免select *。

直接查大表不加限制,99%会卡住或超时;必须用索引+ROWNUM分页+避免SELECT *。
为什么直接SELECT * FROM 大表会慢
Oracle查大表时,如果没走索引、没加条件、没限制行数,就会触发全表扫描(FTS)。几千万行的表,光是读数据块就要消耗大量Buffer Gets和Disk Reads,还会挤占Shared Pool和PGA内存。更麻烦的是,客户端可能等不到结果就断连,而数据库后台还在拼命扫。
- 没WHERE条件 → 全表扫描不可避免
- 用了SELECT * 且字段含LOB/CLOB → 单行数据体积爆炸,网络传输和内存开销剧增
- 没加ROWNUM限制 → 客户端驱动(如PL/SQL Developer)默认拉取全部结果集,容易OOM或假死
- ORDER BY未建索引字段 → 触发磁盘排序(TEMP空间暴涨,SQL执行变龟速)
用ROWNUM做物理分页比OFFSET/LIMIT更稳
Oracle 12c以前没有OFFSET/FETCH语法,即使在19c里,OFFSET 1000000 ROWS FETCH NEXT 1000 ROWS ONLY 在无索引排序下依然要先排完所有数据再截断,性能极差。而ROWNUM是伪列,在查询执行计划生成阶段就参与过滤,只要写法正确,能有效阻止全表扫描。
- ✅ 正确写法:外层套一层子查询,
ROWNUM必须在最内层子查询中限定,比如SELECT * FROM (SELECT ROWNUM r, t.* FROM big_table t WHERE status = 'A') WHERE r BETWEEN 1 AND 1000 - ❌ 错误写法:
SELECT * FROM big_table WHERE ROWNUM —— Oracle先取1000行再过滤status,结果可能为0 - 注意:如果需要按时间倒序取最新1000条,务必给
create_time建索引,并写成ORDER BY create_time DESC+ROWNUM嵌套,否则排序成本不可控
WHERE条件里别让索引失效
哪怕表上有idx_orders_status_time联合索引,只要WHERE里一写函数,索引立刻作废。比如TO_CHAR(create_time, 'YYYYMM') = '202608',优化器只能全表扫描。
- ✅ 改成范围查询:
create_time >= DATE '2026-08-01' AND create_time ,索引可高效range scan - ✅ 绑定变量没问题,但别写
UPPER(name) = :p_name;应建函数索引CREATE INDEX idx_orders_name_upper ON orders (UPPER(name)),或改用name = UPPER(:p_name) - ⚠️ 特别注意
IS NULL、!=、NOT IN这些操作符,它们大概率导致索引跳过;宁可用status IN ('A','B')代替status != 'C'
PL/SQL Developer里查大表的实操红线
工具本身不是问题,但默认配置对大表极不友好。你点“查询数据”那一刻,它就在后台默默发起SELECT * FROM table_name,没加任何保护。
- 务必提前在SQL窗口手动写好带
WHERE和ROWNUM的语句,再执行 - 对象浏览器里右键“查看数据”前,先点顶部菜单「工具 → 首选项 → 窗口类型 → SQL窗口」,把“最大行数”调成1000(别留0)
- 遇到长时间转圈,别硬等——新开一个SQL窗口,跑
SELECT COUNT(*) FROM v$session WHERE status = 'ACTIVE' AND sql_id = 'xxx'看是否真在跑,或者查v$session_longops确认进度 - 如果只是想看数据分布,用
SELECT COUNT(*), status FROM big_table GROUP BY status比扫全表快百倍
真正难的不是写出能跑的SQL,而是判断哪几个字段组合能支撑你的业务查询路径。索引不是越多越好,但缺一个关键复合索引,ROWNUM也救不了你。











