来自相关 table 的值的部分索引,而不是外键?

Partial index on value from related table, rather than foreign key?

我在一个学习平台上工作,学生属于一个 team,每个学生都属于一个 curriculum:

CREATE TABLE teams (
    id SERIAL,
    name string NOT NULL,
    curriculum_id integer NOT NULL
);

CREATE TABLE curricula (
    id SERIAL,
    name string NOT NULL
);

CREATE UNIQUE INDEX index_curricula_on_name ON curricula USING btree (name);

课程的名称必须是唯一的,虽然大多数课程都允许有多个与之关联的团队,但一个不能。我正在尝试在团队中添加部分(唯一)索引 table 以增加对课程的限制。

我知道我可以部分限制课程 ID 本身...

CREATE UNIQUE INDEX index_teams_on_curriculum_id ON teams USING btree (curriculum_id)
WHERE curriculum_id = 1;

...但这不可行,因为课程的 ID 会因环境(开发、暂存等)而异。

有没有办法用 curricula.name 来约束 teams.curriculum_id 列?

您可以使用触发器或 CHECK 约束中的假 immutable 函数来实现类似的功能。两者都有弱点。

但这也可以用 纯 SQL 来实现 - 仅使用 NOT NULLCHECKUNIQUEFK约束。没有弱点。

CREATE TABLE curriculum (
   curriculum_id serial PRIMARY KEY
 , curriculum    text UNIQUE NOT NULL
 , team_unique   boolean UNIQUE NOT NULL
 , CONSTRAINT curriculum_team_uni UNIQUE (curriculum_id, team_unique)  -- for multicolumn FK
);

CREATE TABLE team (
   team_id       serial PRIMARY KEY
 , team          text NOT NULL
 , curriculum_id integer NOT NULL
 , team_unique   boolean NOT NULL
 -- , CONSTRAINT fk1 FOREIGN KEY (curriculum_id) REFERENCES curriculum
 , CONSTRAINT fk2 FOREIGN KEY (curriculum_id, team_unique)
              REFERENCES curriculum (curriculum_id, team_unique)
);

CREATE UNIQUE INDEX team_curriculum_uni_idx ON team (team_unique)
WHERE team_unique;
  • 向父子 table 添加一个 boolean NOT NULL 列,并在父 table 中使其成为 UNIQUE。因此,curriculum 中只有 一个 行可以标记为唯一 - 以实现您的限制性要求:

    one can not

    部分唯一索引 team_curriculum_uni_idx 只强制对其进行单一引用。

    如果有多个唯一课程(仅供参考一次),我们将删除 curriculum.team_unique 上的 UNIQUE 约束并将 team 上的部分唯一索引扩展到 (curriculum_id, team_unique).

  • FK(fk2)强制继承列组合

  • 这使得添加 UNIQUE 约束以针对独特课程实施单个团队变得简单。

  • Postgres FK 约束的默认 MATCH SIMPLE 行为仅强制执行没有 NULL 值的组合。我们可以使用 MATCH FULL 或另一个普通 FK (fk1) 来强制只存在现有的 curriculum_id。我评论了额外的 FK,因为我们在此配置中不需要它(两个 FK 列都定义了 NOT NULL)。

SQL Fiddle.

相关: