postgres 中跨多个表的部分唯一约束

lpd*_*lpd 5 postgresql inheritance unique-constraint

要在 postgres 中强制执行部分唯一性,创建部分唯一索引而不是显式约束是一种众所周知的解决方法,如下所示:

CREATE TABLE possession (
  possession_id serial PRIMARY KEY,
  owner_id integer NOT NULL REFERENCES owner(owner_id),
  special boolean NOT NULL DEFAULT false
);
CREATE UNIQUE INDEX possession_unique_special ON possession(owner_id, special) WHERE special = true;
Run Code Online (Sandbox Code Playgroud)

这将限制每个所有者在数据库级别拥有不超过一个特殊财产。显然,创建跨多个表的索引是不可能的,因此在列存在于不同表中的超类型和子类型情况下,此方法不能用于强制执行部分唯一性。

CREATE TABLE possession (
  possession_id serial PRIMARY KEY,
  owner_id integer NOT NULL REFERENCES owner(owner_id)
);

CREATE TABLE toy (
  possession_id integer PRIMARY KEY REFERENCES possession(possession_id),
  special boolean NOT NULL DEFAULT false
);
Run Code Online (Sandbox Code Playgroud)

如您所见,在此示例中,较早的方法不允许将每个所有者限制为不超过一个特殊玩具。假设每个财产都必须实现一个子类型,那么在不大量改变原始表的情况下在 postgres 中强制执行此约束的最佳方法是什么?

lpd*_*lpd 4

Postgres 直接支持表继承,这可以做到这一点:

CREATE TABLE base_possession (
  possession_id SERIAL PRIMARY KEY
);

CREATE TABLE possession (
  possession_id INTEGER NOT NULL REFERENCES base_possession(possession_id),
  owner_id INTEGER NOT NULL REFERENCES owner(owner_id)
);

CREATE TABLE toy (
  special boolean NOT NULL DEFAULT false,
  PRIMARY KEY (possession_id),
  FOREIGN KEY (possession_id) REFERENCES base_possession(possession_id),
  FOREIGN KEY (owner_id) REFERENCES owner(owner_id)
) INHERITS (possession);
CREATE UNIQUE INDEX toy_unique_special ON toy(owner_id, special) WHERE special = true;
Run Code Online (Sandbox Code Playgroud)