truncate table几乎不写redo,因其仅记录段头重置等元数据操作,而非逐行删除日志;5000万行仅生成几kb redo,远少于delete的海量日志。

TRUNCATE TABLE 是唯一能真正绕过 Redo 日志、秒级清空大表的原生命令,前提是你要清空整张表,且不依赖事务回滚、不触发 DELETE 触发器、也不需要保留自增序列当前值。
TRUNCATE TABLE 为什么几乎不写 Redo?
它不是逐行删除,而是直接释放数据段(data segment)和重置高水位线(HWM)。Oracle 只在 Redo 中记录“段头被重置”这一条元数据操作,而非每行数据的删除动作。所以哪怕删 5000 万行,Redo 体积也基本固定在几 KB 级别。
对比 DELETE FROM table_name:每删一行都生成 Undo + Redo 条目,5000 万行可能产生几十 GB Redo,还容易撑爆 UNDO 表空间或触发归档风暴。
- 必须用
TRUNCATE TABLE,不能写成TRUNCATE或TRUNCATE table(缺少TABLE关键字会报错ORA-00900: invalid SQL statement) - 执行用户需是表所有者,或拥有
ALTER ANY TABLE或DROP ANY TABLE权限 - 若表被外键引用,需先禁用约束(
ALTER TABLE child_table DISABLE CONSTRAINT fk_name),或加CASCADE子句(Oracle 12c+ 支持:TRUNCATE TABLE parent_table CASCADE)
TRUNCATE 后序列(SEQUENCE)值被重置,怎么补救?
是的,TRUNCATE TABLE 会把关联的 IDENTITY 列或手动维护的序列计数器“打回原形”,从初始值(如 1)重新开始。这不是 Bug,是设计行为。
如果你依赖序列值连续或不重用,不能靠 TRUNCATE。此时只能退回到 DELETE + 手动 ALTER SEQUENCE RESTART START WITH N,但代价是性能暴跌。
- 查当前最大 ID:
SELECT NVL(MAX(id), 0) FROM your_table - 重设序列:
ALTER SEQUENCE your_seq RESTART START WITH 1234567(注意:12cR2+ 才支持RESTART;老版本用INCREMENT BY+NEXTVAL+ 再改回步长) - 但请再确认一次:你真的需要保留这个序列值?多数业务场景下,ID 唯一性比连续性重要得多
批量清空多个表时,PL/SQL 循环里要注意什么?
别手写一堆 TRUNCATE TABLE t1; TRUNCATE TABLE t2; —— 遇到权限不足或表不存在就中断。用动态 SQL + 异常捕获更稳:
DECLARE
v_sql VARCHAR2(200);
BEGIN
FOR r IN (SELECT table_name FROM user_tables WHERE tablespace_name = 'USERS') LOOP
v_sql := 'TRUNCATE TABLE ' || r.table_name;
BEGIN
EXECUTE IMMEDIATE v_sql;
DBMS_OUTPUT.PUT_LINE('Truncated: ' || r.table_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Skip ' || r.table_name || ': ' || SQLERRM);
END;
END LOOP;
END;
- 务必用
user_tables或dba_tables过滤真实存在的表,避免ORA-00942: table or view does not exist - 不要在循环里漏掉
COMMIT——TRUNCATE是 DDL,自动提交,无需也不该手动加 - 如果某张表有未提交的事务锁住,
TRUNCATE会直接报ORA-00054: resource busy,得先 kill 对应 session
真正容易被忽略的是权限粒度和外键联动——很多人卡在 ORA-00942 或 ORA-02266 上,却以为是命令写错了。其实问题不在 TRUNCATE 本身,而在它暴露了对象可见性与约束拓扑的真实状态。











