18 ALTER TABLE 与 DROP
表会不断演化。ALTER TABLE 用于修改已有表:添加或删除列,重命名列或表本身,添加或删除约束,修改列的类型,修改列的默认值。DROP TABLE 会删除表及其包含的每一行。PostgreSQL 中的模式变更具有事务性——ALTER TABLE 失败时会正常回滚,你还可以把多个 ALTER 放进 BEGIN ... COMMIT,以原子方式应用它们。
本章要点
- 添加、重命名和删除列
- 添加和删除约束
- 使用
USING修改列的类型 - 重命名表
- 常见修改操作对锁和表重写的影响
示例
每条 ALTER TABLE 都是一个独立的事务性语句。你也可以批量执行多个操作:ALTER TABLE t ADD COLUMN a int, ADD COLUMN b text, DROP COLUMN c;——把它们放在一条语句中运行会更快,因为表只需被访问一次。
有两种操作值得从代价角度理解:
- 添加没有默认值(或默认值为常量)的列很快——PostgreSQL 只需记录新列的元数据。
- 许多列类型变更都会重写表。
ALTER COLUMN x TYPE bigint会读取并重写每一行。在大表上,这可能耗时数小时,并在整个过程中持有排他锁。有些二进制兼容的变更可以避免重写。对于重写量大的变更,应规划如下迁移:添加新列 → 回填 → 切换;pg_repack不能执行任意类型转换。
ALTER TABLE ... ALTER COLUMN col TYPE new_type USING expr 允许你指定如何转换现有值:ALTER COLUMN price TYPE numeric(12,2) USING price::numeric(12,2)。
运行示例
运行方式:
常见陷阱
- 向现有列添加
NOT NULL时,要求该列中已经没有任何空值,并且会获取ACCESS EXCLUSIVE锁,一直持有到验证完成。先回填,再SET NOT NULL。从 PostgreSQL 12 开始,如果存在匹配的CHECK约束,ALTER COLUMN x SET NOT NULL可以更快完成。 - 删除列不会立即回收磁盘空间——该列只会被标记为无效。运行
VACUUM FULL(重量级操作)或重新创建表,才能回收空间。 - 重命名列会更新由系统目录跟踪的依赖关系,例如视图,但可能破坏应用程序 SQL 和以文本形式存储的函数体。重命名之前请检查这些用法。
- 对
ENUM执行ALTER TYPE时,不能移除已有值;添加值则没有问题。
对于零停机的模式变更,标准做法是:添加新列 → 分批回填 → 让应用同时写入两列 → 切换读操作 → 删除旧列。Liquibase、Flyway 或 pg_repack 等工具可以提供帮助;数据库负责加锁,你负责编排各个步骤。
先加回一列:ALTER TABLE products ADD COLUMN notes text;。然后重命名:ALTER TABLE products RENAME COLUMN notes TO remarks;。再删除:ALTER TABLE products DROP COLUMN remarks;。在 psql 中用 \d products 检查。
下载
SQL 文件:alter-table-and-drop.sql · 全部示例文件 · 准备工作