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

时间:2016-02-22 19:15:07

标签: sql postgresql database-design constraints postgresql-9.4

我正在开设一个学习平台,学生属于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);

课程必须是名字独一无二的,虽然大多数课程都允许多个团队与他们相关联,但是不能。我正在尝试在团队表上添加部分(唯一)索引,以便在课程表上添加约束。

我知道我可以用...来部分约束课程ID本身。

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

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

有没有办法将teams.curriculum_id列限制为curricula.name

1 个答案:

答案 0 :(得分:3)

您可以使用触发器或CHECK约束中的伪不可变函数来实现此类内容。两者都有自己的弱点。

但这也可以通过纯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;
  • boolean NOT NULL列添加到父表和子表,并在父表中将其设为UNIQUE。因此,只有curriculum中的一行行可以标记为唯一 - 以实现您的限制性要求:

      

    一个人不能

    部分唯一索引team_curriculum_uni_idx仅强制对其进行一次引用。

    如果有多个唯一的课程(仅引用一次),我们会删除UNIQUE上的curriculum.team_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.

相关: