将主键更改为复合主键

Posted

技术标签:

【中文标题】将主键更改为复合主键【英文标题】:Changing a primary key to a composite primary key 【发布时间】:2015-01-08 02:47:08 【问题描述】:

我一直在为我的一张表使用“普通”(非复合)主键。现在我想将其更改为复合主键。 我的表格看起来像这样:

-- Table 1
CREATE TABLE foo (
    id SERIAL PRIMARY KEY,
    id2 INT,
    ...
)

-- Table 2
CREATE TABLE bar (
    id SERIAL PRIMARY KEY,
    id_foo INT REFERENCES foo (id)
)

这里的问题是 psql 不想删除旧的主键,因为其他表引用它。

有没有办法在不删除整个数据库的情况下解决这个问题?

【问题讨论】:

【参考方案1】:

您可以在删除PRIMARY KEY 约束之前在id 上添加一个多余的UNIQUE constraint。这满足了 FK 约束的要求。 Per documentation:

外键必须引用作为主键或形成唯一约束的列。

我的大胆强调。

显然,FK 约束绑定到它在 pg_depend 中显式创建的 PK 约束。因此,您需要删除并稍后重新创建所有引用 FK 约束或弄乱系统表(这是不可取的!)。 一个事务中最好的保持引用完整性完整:

BEGIN;
ALTER TABLE bar DROP CONSTRAINT bar_id_foo_fkey;

ALTER TABLE foo 
    DROP CONSTRAINT foo_pkey
  , ADD CONSTRAINT foo_uni_id UNIQUE (id)
  , ADD PRIMARY KEY (id, id2);

ALTER TABLE bar ADD CONSTRAINT bar_id_foo_fkey
  FOREIGN KEY (id) REFERENCES foo (id);

COMMIT;

SQL Fiddle.(查询显示目录条目,提供名称等。)

manual on ALTER TABLE中的详细信息。

这仅作为中间状态才有意义。如果id 保持UNIQUE NOT NULL,那还不如PK。

【讨论】:

如果我理解正确,CREATE UNIQUE index indexname ON foo(id);应该管用。我试过了,它仍然不允许我删除主键。 @Seleren:不,你需要一个独特的约束,这是微妙的不同。添加代码示例。 听起来像是正确的答案,但由于我至少有 10 个外键引用了我想在其中创建复合键的表,所以这对我来说效果不佳。不过,感谢您抽出宝贵的时间。 @Seleren:您可以从目录文件生成脚本。具有默认功能的 FK 约束的基本示例:***.com/questions/12395466/…。【参考方案2】:

我想您会想要更改所有引用外键以使用新的主键,对吧?所以你将不得不删除所有这些外键,然后删除主键,然后创建新的主键,然后创建引用新主键的新外键。因此,如果有 10 个 FK 引用此表,则您必须执行 10 次 FK 下降,1 次旧 PK 下降,1 次变更以添加新 PK,然后 10 次变更以添加新 FK。

我手边没有 Postgres 实例,但我认为没有捷径可走。我认为你必须完成所有 22 个步骤。但是您不必删除数据库,只需删除所有 FK。

如果您希望旧 FK 继续指向旧 PK,完成后将不再是 PK,那么正如 Erwin 所说,您应该能够在其上添加唯一约束。尽管我认为他的示例是倒退的,因为您必须在删除 PK 之前添加唯一约束,否则您会收到相同的消息。正如我所说,没有 Postgres 的副本可以方便地检查这一点。在 Sql Server 中,您可以添加唯一索引,然后删除 pk,然后创建新 pk。我想最坏的情况是您必须删除 10 个 FK,然后重新创建它们。

【讨论】:

没错,但由于我使用的是数据库设计器,所以我只是删除了整个数据库并重新创建它,比删除所有 FK 的工作量更少。

以上是关于将主键更改为复合主键的主要内容,如果未能解决你的问题,请参考以下文章

主键映射

Hibernate复合主键映射

Hibernate复合主键映射

用于更改表添加复合主键的批量 SQL 语句

将整数类型的所有主键更改为 bigint,包括引用

mysql的联合主键与复合主键区别