我正在开设一个学习平台,学生属于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
?
答案 0 :(得分:3)
您可以使用触发器或CHECK
约束中的伪不可变函数来实现此类内容。两者都有自己的弱点。
但这也可以通过纯SQL 实现 - 仅使用NOT NULL
,CHECK
,UNIQUE
和FK
约束。没有弱点。
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
)。
相关: