DELETE后查询变慢主因是高水位线(HWM)未降低,Oracle仅标记删除而不释放空间,导致全表扫描仍遍历空块;可用ALTER TABLE MOVE降HWM但需重建索引,并及时更新统计信息和清理执行计划缓存。
delete 后查询变慢,大概率是高水位线(HWM)没降
oracle 的 delete 不动高水位线,只标记数据为“已删除”。哪怕你删掉 99% 的数据,select 仍要扫描 hwm 下所有数据块——哪怕那些块全是空的。比如一千万行删到只剩 32 行,查询仍扫一千万行对应的块,耗时从 0.08 秒跳到 34 秒,就是这个原因。
这不是索引问题,也不是统计信息没更新,而是物理存储结构没重排。你可以用以下语句确认当前 HWM 位置:
SELECT blocks, num_rows, avg_row_len FROM user_tables WHERE table_name = 'YOUR_TABLE';
BLOCKS 值远大于 NUM_ROWS × AVG_ROW_LEN / DB_BLOCK_SIZE 的估算值,就说明存在严重空间浪费。
alter table ... move 能降 HWM,但会失效索引
ALTER TABLE your_table MOVE 是最直接降低 HWM 的方式:它把所有剩余行重新紧凑写入新数据块,清空旧块,HWM 自然回落。但副作用明显:
-
ROWID全部变更,所有基于ROWID的索引(即普通 B-Tree 索引)自动变为UNUSABLE - 后续任何走这些索引的查询或 DML 都会报
ORA-01502 - 主键、唯一约束依赖的索引也会失效,可能导致插入失败
执行后必须立刻重建索引,不能跳过。常用操作是:
ALTER INDEX idx_name REBUILD;
如果表有几十个索引,逐个 REBUILD 很耗时;可加 PARTITION 或 PARALLEL 缩短时间,但要注意并发资源占用。
TRUNCATE 比 DELETE 快,但不可回滚且不触发触发器
如果你本意就是清空大部分数据(比如只留最近一天),TRUNCATE TABLE 是比 DELETE 更合适的选择:
- 瞬间重置 HWM 到最低,不生成回滚日志,不走 UNDO 表空间
- 不触发
ON DELETE触发器,也不检查外键约束(除非加CASCADE) - 无法回滚,也不能带
WHERE条件——所以不能“删旧留新”,只能全清或配合分区
若需保留部分数据,又不想重建整个表,优先考虑按时间字段分区。例如:
ALTER TABLE log_table DROP PARTITION p_2025_q1;
这种 DDL 级别删分区,既降 HWM 又不锁全表,还不影响其他分区索引。
真正容易被忽略的是统计信息和执行计划缓存
即使你成功 MOVE + REBUILD,查询仍可能慢——因为优化器还在用旧的统计信息做判断。执行完物理收缩后,务必手动收集:
EXEC DBMS_STATS.GATHER_TABLE_STATS('OWNER', 'YOUR_TABLE');
另外,如果该表在大量 SQL 中被引用,其旧执行计划可能还缓存在 shared pool。可以针对性刷新:
SELECT address, hash_value FROM v$sqlarea WHERE sql_text LIKE '%YOUR_TABLE%';
再用 DBMS_SHARED_POOL.PURGE 清掉对应句柄(需 ADMINISTER DATABASE TRIGGER 权限)。否则哪怕数据和索引都对了,优化器仍可能选错访问路径。



















