首页 / PostgreSQL 入门教程 / 删除与清空表(DROP/TRUNCATE)

PostgreSQL 入门教程

删除与清空表(DROP/TRUNCATE)

本教程共 50 篇 · 第 10 篇 · 更新于 2026-07-31 · 约 7 分钟阅读

PostgreSQLPostgreSQL 入门教程DROP TABLETRUNCATEDELETECASCADE

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 的区别

对比项TRUNCATEDELETE(不带 WHERE)
速度快,最小日志慢,逐行记日志
触发 ON DELETE 触发器
重置自增计数器RESTART IDENTITY
可加条件删除部分行
事务内可回滚

简单说:清空整张表用 TRUNCATE;只删一部分、或要触发触发器,用 DELETE

常见误区

不少人以为 TRUNCATE 执行了就没法恢复,这是错的。它和普通 DELETE 一样受事务保护,只要还没 COMMITROLLBACK 就能找回数据。真正危险的是 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。尤其是 TRUNCATEDROP,一旦提交,恢复成本远高于多花那几秒确认。

还有个容易忽略的点:TRUNCATE 不能带 WHERE,所以「清空大部分、留一小部分」这种需求它做不到,只能回到 DELETE ... WHERE。反过来,DELETE 全表很慢,大表请忍住别用 DELETE FROM 大表 去清空,改用 TRUNCATE

空间回收与防误删

TRUNCATE 直接丢弃数据页,空间立刻回到表自身可重用;DROP TABLE 删掉整张表后,原空间由自动清理(autovacuum)慢慢回收,不会马上还给操作系统,但都不影响你立刻重建同名表。

一旦误删,靠什么救:

  1. 定期备份:最简单可靠的兜底,pg_dump 能整表恢复。
  2. 事务未提交:TRUNCATE/DELETECOMMIT 前都能 ROLLBACK,养成先 BEGIN 试跑的习惯。
  3. 时间点恢复(PITR):用基础备份加 WAL 日志,可恢复到误删前的任意一秒,是生产环境的标配防护。
Warning

DROP TABLE 提交后没有「撤销」魔法,只能靠备份或 PITR 找回。生产执行前务必确认备份最新且可恢复。

一个外键级联的例子

CASCADE 到底删了什么,用实例最清楚。假设 usersorders 用外键引用:

-- 清空 users 时,顺带清空引用它的 orders
TRUNCATE TABLE users CASCADE;

-- 删 users 表时,顺带删掉指向它的外键约束
DROP TABLE users CASCADE;

第一个 TRUNCATE ... CASCADEorders 的数据也清了;第二个 DROP ... CASCADEorders 上的外键约束也删了。两个 CASCADE 删的对象不同,但都「波及别人」,执行前要想清楚波及范围。

怎么选

  1. 彻底不要这张表 → DROP TABLE(加 IF EXISTS 更稳,删依赖对象要 CASCADE)。
  2. 清空数据但保留结构 → TRUNCATE,大表尤其推荐,需要归零自增就加 RESTART IDENTITY
  3. 只删符合某条件的行 → DELETE ... WHERE,它能触发触发器也能回滚。

记住一条铁律:任何 DROPTRUNCATE 上线前,先确认有备份或能承受丢失。生产环境建议先用事务包起来,验证无误再提交。