复合 FK 引用原子 PK + 非唯一属性

Composite FK referencing atomic PK + non unique attribute

我正在尝试在 Postgres 13.3 中创建以下 table:

CREATE TABLE IF NOT EXISTS accounts (
    account_id Integer PRIMARY KEY NOT NULL
);

CREATE TABLE IF NOT EXISTS users (
    user_id Integer PRIMARY KEY NOT NULL,
    account_id Integer NOT NULL REFERENCES accounts(account_id) ON DELETE CASCADE
);

CREATE TABLE IF NOT EXISTS calendars (
    calendar_id Integer PRIMARY KEY NOT NULL,
    user_id Integer NOT NULL,
    account_id Integer NOT NULL,
    FOREIGN KEY (user_id, account_id) REFERENCES users(user_id, account_id) ON DELETE CASCADE
);

但是我在创建日历时遇到以下错误 table:

ERROR:  there is no unique constraint matching given keys for referenced table "users"

这对我来说没有多大意义,因为外键包含 user_id,它是用户 table 的 PK,因此也有唯一性约束。如果我在组合 user_id 和 account_id 上添加显式唯一性约束,如下所示:

ALTER TABLE users ADD UNIQUE (user_id, account_id);

然后我就可以创建日历 table。这种独特的约束对我来说似乎是不必要的,因为 user_id 已经是独一无二的了。有人可以向我解释一下我在这里缺少什么吗?

Postgres 是如此 smart/dumb 以至于它不会假定设计者会做愚蠢的事情。

Postgres 设计者可以采取不同的策略:

  • 检测传递性,使FK不仅依赖于users.id,还依赖于users.account_id -> accounts.id。这是可行的,但成本很高。它还涉及在目录中为单个 FK 约束添加多个依赖项记录。施加约束(在两个引用的 table 中的任何一个中进行更新或删除)时,它可能会变得非常复杂。
  • 检测传递性,默默忽略冗余列引用。这意味着:对程序员说谎。它还需要在目录中表示。
  • 级联 DDL 操作也会变得更加复杂。 (记住:DDL已经很辛苦了w.r.t。concurrency/versioning)

从 execution/performance 的角度来看:施加约束目前涉及对引用的 table 索引的“伪触发器”。 (DEFERRED除外,需要特殊处理)

因此,恕我直言,Postgres 开发人员做出了明智的选择,拒绝做愚蠢的复杂事情。