删除与清空表(DROP/TRUNCATE)
本教程共 50 篇 · 第 10 篇 · 更新于 2026-07-31 · 约 7 分钟阅读
10. 删除与清空表(DROP/TRUNCATE)
本节目标:学完能分清「删表」「清空数据」「逐行删除」三种操作,知道什么时候该用哪个。
数据清理由浅到深有三档:DELETE 删部分行、TRUNCATE 清空整张表、DROP TABLE 连表带结构一起删。本章重点讲后两者,因为「删错了」的代价最大。我之前就见过有人想清测试数据,手一抖写成 DROP TABLE,整张表没了,还好有备份。所以这三种操作的分寸一定要拿稳。
删除表(DROP TABLE)
DROP TABLE 会把表的结构、数据、索引、约束一次性删干净,不可恢复。它是破坏性最强的操作。
DROP TABLE old_logs;
表不存在时直接报错。加 IF EXISTS 只提示、不报错,写脚本时更稳:
DROP TABLE IF EXISTS old_logs;
一次删多张表,用逗号隔开:
DROP TABLE users, orders;
CASCADE 要慎用
如果别的表用外键指向它,直接删会失败。加 CASCADE 把依赖对象也一起删:
DROP TABLE users CASCADE;
Warning
CASCADE会顺手删掉引用它的外键约束等相关对象,可能把别的表的关联也切断。删之前务必确认这些依赖可以不要,最好先用\d看看谁依赖这张表。误删的依赖往往很难凭记忆复原。
默认行为其实是 RESTRICT:只要有依赖就拒绝删,所以平时不加 CASCADE 是安全的。删完之后,表占用的磁盘空间不会立刻还给操作系统,需要后续的 VACUUM 或自动清理来回收,不过这不影响你重新建同名表。
清空表(TRUNCATE)
只想清空数据、保留表结构,用 TRUNCATE。它比 DELETE 快很多,因为不逐行记日志,而是直接释放数据页。
TRUNCATE TABLE orders;
一次清空多张表:
TRUNCATE TABLE users, orders;
重置自增计数器
自增列(identity)的值默认不清零,下次插入还是接着之前的号。要重置,加 RESTART IDENTITY:
TRUNCATE TABLE orders RESTART IDENTITY;
如果只想清空数据、又不想动自增的计数,省略 RESTART IDENTITY(等价于默认的 CONTINUE IDENTITY)即可,计数会继续保持:
TRUNCATE TABLE orders CONTINUE IDENTITY; -- 自增号不归零
被外键引用时
如果表被别的表的外键引用,直接 TRUNCATE 会报错。加 CASCADE 同时清空引用方:
TRUNCATE TABLE users CASCADE; -- 同时清空引用 users 的 orders
Tip
TRUNCATE不走逐行删除,所以不会触发ON DELETE触发器。需要靠触发器做清理的逻辑要留意这点。但它确实更快,大表清空数据几乎都该用它,而不是DELETE FROM 大表。
Note
TRUNCATE是事务安全的,放在事务里照样能回滚,别以为它「不能撤」。下面的命令可以放心试:
BEGIN;
TRUNCATE TABLE orders;
ROLLBACK; -- 数据还在
TRUNCATE 与 DELETE 的区别
| 对比项 | TRUNCATE | DELETE(不带 WHERE) |
|---|---|---|
| 速度 | 快,最小日志 | 慢,逐行记日志 |
| 触发 ON DELETE 触发器 | 否 | 是 |
| 重置自增计数器 | 需 RESTART IDENTITY | 否 |
| 可加条件删除部分行 | 否 | 是 |
| 事务内可回滚 | 是 | 是 |
简单说:清空整张表用 TRUNCATE;只删一部分、或要触发触发器,用 DELETE。
常见误区
不少人以为 TRUNCATE 执行了就没法恢复,这是错的。它和普通 DELETE 一样受事务保护,只要还没 COMMIT,ROLLBACK 就能找回数据。真正危险的是 DROP TABLE,那才是不进回收站的直接删除,连结构一起没。另外,TRUNCATE 不能带 WHERE 条件,想删一部分数据只能回到 DELETE ... WHERE。
一个对比小例子
-- 假设 orders 已有 id 1~1000,amount 各异
-- 方式一:逐行删,慢但能触发触发器、能加条件
DELETE FROM orders WHERE amount < 10;
-- 方式二:整表清空,快且不触发触发器
TRUNCATE TABLE orders;
-- 方式三:连表带结构删除,最彻底
DROP TABLE IF EXISTS orders;
三种写法天差地别,下命令前先确认自己要的到底是哪一个。
三步法:先想清要哪种
动手前用三个问题自检:这张表还要吗?只要数据不要结构吗?只删一部分行吗?答案对应 DROP / TRUNCATE / DELETE。我习惯先在测试库用 BEGIN 包起来跑一遍,确认影响的行数、表符合预期,再 COMMIT。尤其是 TRUNCATE 和 DROP,一旦提交,恢复成本远高于多花那几秒确认。
还有个容易忽略的点:TRUNCATE 不能带 WHERE,所以「清空大部分、留一小部分」这种需求它做不到,只能回到 DELETE ... WHERE。反过来,DELETE 全表很慢,大表请忍住别用 DELETE FROM 大表 去清空,改用 TRUNCATE。
空间回收与防误删
TRUNCATE 直接丢弃数据页,空间立刻回到表自身可重用;DROP TABLE 删掉整张表后,原空间由自动清理(autovacuum)慢慢回收,不会马上还给操作系统,但都不影响你立刻重建同名表。
一旦误删,靠什么救:
- 定期备份:最简单可靠的兜底,
pg_dump能整表恢复。 - 事务未提交:
TRUNCATE/DELETE在COMMIT前都能ROLLBACK,养成先BEGIN试跑的习惯。 - 时间点恢复(PITR):用基础备份加 WAL 日志,可恢复到误删前的任意一秒,是生产环境的标配防护。
Warning
DROP TABLE提交后没有「撤销」魔法,只能靠备份或 PITR 找回。生产执行前务必确认备份最新且可恢复。
一个外键级联的例子
CASCADE 到底删了什么,用实例最清楚。假设 users 被 orders 用外键引用:
-- 清空 users 时,顺带清空引用它的 orders
TRUNCATE TABLE users CASCADE;
-- 删 users 表时,顺带删掉指向它的外键约束
DROP TABLE users CASCADE;
第一个 TRUNCATE ... CASCADE 把 orders 的数据也清了;第二个 DROP ... CASCADE 把 orders 上的外键约束也删了。两个 CASCADE 删的对象不同,但都「波及别人」,执行前要想清楚波及范围。
怎么选
- 彻底不要这张表 →
DROP TABLE(加IF EXISTS更稳,删依赖对象要CASCADE)。 - 清空数据但保留结构 →
TRUNCATE,大表尤其推荐,需要归零自增就加RESTART IDENTITY。 - 只删符合某条件的行 →
DELETE ... WHERE,它能触发触发器也能回滚。
记住一条铁律:任何 DROP 和 TRUNCATE 上线前,先确认有备份或能承受丢失。生产环境建议先用事务包起来,验证无误再提交。