TRUNCATE重置高水位线(HWM)并释放段空间,是隐式提交的DDL操作;DELETE仅标记行删除、HWM不变,属可回滚的DML。TRUNCATE不可带WHERE、不触发触发器、不能直接用于PL/SQL(需EXECUTE IMMEDIATE),而DELETE支持条件、事务控制和触发器。
TRUNCATE 会重置高水位线,DELETE 不会
清空表数据时,truncate table 本质是 ddl 操作,直接释放段(segment)空间并重置高水位线(hwm),后续 insert 会从块头重新开始写;而 delete from 是 dml,逐行标记为已删除,hwm 不动,表空间占用不变,甚至可能因未提交导致回滚段膨胀。
常见错误现象:DELETE FROM t1 后执行 SELECT COUNT(*) FROM t1 返回 0,但查询 USER_TABLES.BLOCKS 或执行全表扫描仍很慢——说明 HWM 还卡在高位。
- 如果目标是“快速清空 + 节省后续插入开销”,选
TRUNCATE - 如果需触发器、需事务控制(比如中途能回滚)、或表被外键引用(且未加
CASCADE),只能用DELETE -
TRUNCATE无法带 WHERE 条件;DELETE可以加条件,但清空全表时性能远差于TRUNCATE
TRUNCATE 在 PL/SQL 中不能直接执行,必须用 EXECUTE IMMEDIATE
PL/SQL 块里写 TRUNCATE TABLE t1 会报错 PLS-00103: Encountered the symbol "TRUNCATE",因为它是 DDL,不被 PL/SQL 引擎原生支持。
正确做法只有一种:动态 SQL。
- 必须用
EXECUTE IMMEDIATE 'TRUNCATE TABLE t1' - 不能拼接变量名而不加引号——
'TRUNCATE TABLE ' || v_table_name有 SQL 注入风险,应配合DBMS_ASSERT.SQL_OBJECT_NAME -
TRUNCATE隐式提交,执行后无法回滚,哪怕它在存储过程中、哪怕前面刚INSERT了数据
示例:
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || DBMS_ASSERT.SQL_OBJECT_NAME(v_table_name);
DELETE 的 WHERE 条件和事务行为直接影响性能与锁粒度
全表 DELETE FROM t1 看似简单,但实际会持有全表行级锁(直到 COMMIT),阻塞其他会话对任意行的 DML;而 TRUNCATE 锁的是整个表结构(DDL 锁),持续时间极短,但会中断所有正在访问该表的事务。
使用场景差异明显:
- 要保留归档日志、做闪回查询(Flashback Query),只能用
DELETE(TRUNCATE不记 undo) - 大表清空时,
DELETE可能因 undo 表空间不足报ORA-01555;分批DELETE(如每次 10000 行 +COMMIT)可缓解,但 HWM 依然不动 -
DELETE会触发BEFORE/AFTER ROW触发器;TRUNCATE完全绕过触发器
高水位线残留会导致执行计划误判和统计信息失效
执行 DELETE 清空后,即使 ANALYZE TABLE 或 DBMS_STATS.GATHER_TABLE_STATS 更新了行数(NUM_ROWS = 0),Oracle 优化器仍可能因 HWM 高而选择全表扫描而非索引访问——尤其当谓词选择率低但表块数(BLOCKS)仍很大时。
这不是 bug,是优化器基于物理存储的合理推断。解决办法不是反复收集统计信息,而是:
- 确认是否真需要保留 HWM(例如测试环境反复加载数据,想模拟真实碎片)
- 若不需要,
TRUNCATE是最干净的解法;否则只能ALTER TABLE ... SHRINK SPACE(需行移动启用,且仍是 DDL) -
SHRINK SPACE会移动数据、重置 HWM,但会产生大量 redo 和临时空间消耗,线上慎用
真正容易被忽略的点:HWM 不是元数据字段,查不到“当前 HWM 值”,只能通过 DBA_SEGMENTS.BLOCKS 和 DBA_TABLES.EMPTY_BLOCKS 间接估算,而后者在 12c+ 已弃用——这意味着你得靠执行计划里的 TABLE ACCESS FULL 成本反推。

















