PostgreSQL分区表先加唯一索引再设主键失败问题分析
multiple primary keys for table X are not allowed 原因解析 场景1失败的核心原因
当你执行create unique index pk_tst_t on tst_t using btree (c1);时,PostgreSQL会自动在所有子分区(这里就是tst_t_988)上生成对应的分区唯一索引。而tst_t_988已经提前创建了主键pk_tst_t_988——主键本质就是带NOT NULL约束的唯一索引,这意味着此时分区上已经有一个唯一索引了。
接下来执行alter table tst_t add primary key (c1);时,PostgreSQL会尝试将全局主键与已有的唯一索引关联,但因为分区上已经存在一个主键(唯一索引),同时全局唯一索引又在分区上生成了另一个唯一索引,PostgreSQL试图把后者标记为主键时,就会触发“不允许多个主键”的报错——因为一个表只能有一个主键约束。
场景2成功的逻辑
直接执行alter table tst_t add primary key (c1);时,PostgreSQL会先检查每个子分区的现有主键是否与全局主键的列匹配(这里都是c1)。由于tst_t_988已经有了c1列的主键,PostgreSQL会直接复用这个分区主键,不会在分区上创建新的索引,自然不会出现冲突,所以操作成功。
场景3成功的关键
create unique index pk_tst_t on only tst_t using btree (c1);中的ONLY关键字是核心:它表示仅在分区表本身创建索引,不会同步到任何子分区。此时执行加主键操作,PostgreSQL会复用这个仅在分区表上的唯一索引,同时验证分区上的主键已经符合全局主键的列要求,不需要在分区上创建新索引,因此不会触发重复主键的冲突。
总结
- 给分区表创建不带
ONLY的唯一索引,会自动在所有子分区生成对应索引,若分区已有主键,会导致分区上存在两个唯一索引,后续加全局主键时触发冲突。 - 直接给分区表加主键,PostgreSQL会智能复用分区已有的匹配主键,无需创建新索引。
- 使用
ONLY关键字创建分区表索引,可避免索引同步到子分区,从而规避冲突。
内容的提问来源于stack exchange,提问作者Peter

