使用Play Framework Evolutions修改PostgreSQL单主键为复合键的疑问
问题描述
我需要使用Scala的Play Framework Evolutions,将PostgreSQL中的PRIMARY KEY CONSTRAINT从单主键修改为复合键。参考《Change Primary Key》一文的示例,我的初始数据库演进文件1.sql内容如下:
-- !Ups CREATE TABLE city ( city_id BIGSERIAL PRIMARY KEY, group_id int, "version" int ); CREATE INDEX city__version ON city("version"); CREATE INDEX city__group_id ON city(group_id); -- !Downs DROP TABLE city;
我希望修改后的表结构将主键改为包含city_id、group_id、version的复合键,如下所示:
CREATE TABLE city ( city_id BIGSERIAL, group_id int, "version" int, CONSTRAINT city_pk PRIMARY KEY (city_id, group_id, "version") ); -- ...
我自己编写了2.sql的初稿,但存在疑问:
-- !Ups ALTER TABLE city DROP COLUMN city_id, ADD COLUMN city_id BIGSERIAL, ADD CONSTRAINT city_pk PRIMARY KEY (city_id, group_id, "version"); -- !Downs ALTER TABLE city DROP CONSTRAINT city_pk, DROP COLUMN city_id, ADD COLUMN city_id BIGSERIAL PRIMARY KEY;
- 因为要删除主键列,是否需要在
ALTER TABLE命令前添加TRUNCATE命令?(我的场景允许截断表) - 有没有无需删除再添加列的方法直接修改PRIMARY KEY?
解决方案
方案一:允许截断表的简化操作
既然你的场景允许清空表数据,完全可以走更简洁的流程,不用折腾列的删除和添加:
-- !Ups -- 清空表数据 TRUNCATE TABLE city; -- 删除原有的单主键约束(PostgreSQL会给隐式主键自动命名,比如city_pkey,可通过\d city命令查看实际名称) ALTER TABLE city DROP CONSTRAINT city_pkey; -- 添加复合主键约束 ALTER TABLE city ADD CONSTRAINT city_pk PRIMARY KEY (city_id, group_id, "version"); -- !Downs -- 清空表数据 TRUNCATE TABLE city; -- 删除复合主键约束 ALTER TABLE city DROP CONSTRAINT city_pk; -- 重新添加单主键约束 ALTER TABLE city ADD CONSTRAINT city_pkey PRIMARY KEY (city_id);
- 原表的
city_id是BIGSERIAL类型,本身就是自增序列,不需要删除重建,直接修改主键约束即可 - 用
TRUNCATE清空数据后,修改主键约束不会有数据冲突,操作更高效
方案二:不截断表的标准修改流程(供参考)
如果后续场景不允许截断表,也可以直接修改主键,无需动列:
-- !Ups -- 删除原主键约束 ALTER TABLE city DROP CONSTRAINT city_pkey; -- 添加复合主键(需确保现有数据中(city_id, group_id, "version")组合无重复) ALTER TABLE city ADD CONSTRAINT city_pk PRIMARY KEY (city_id, group_id, "version"); -- !Downs -- 删除复合主键约束 ALTER TABLE city DROP CONSTRAINT city_pk; -- 恢复单主键约束 ALTER TABLE city ADD CONSTRAINT city_pkey PRIMARY KEY (city_id);
注意:执行该方案的Ups操作前,必须确保现有表中(city_id, group_id, "version")的组合没有重复值,否则会执行失败。
关于你初稿的问题
你原来的DROP COLUMN再ADD COLUMN完全没必要——city_id是我们需要保留在复合主键里的列,删除重建只会丢失原有数据(哪怕截断表也属于冗余操作),而且BIGSERIAL会重新创建序列,纯粹多此一举。
内容的提问来源于stack exchange,提问作者Ricardo
相关产品推荐
相关产品推荐

