来自相关 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 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;
向父子 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
)。
相关:
- MATCH FULL vs MATCH SIMPLE in foreign key constraints
- CONSTRAINT to check values from a remotely related table (via join etc.)
- Disable all constraints and table checks while restoring a dump
- Enforcing constraints “two tables away”
我在一个学习平台上工作,学生属于一个 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 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;
向父子 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
)。
相关:
- MATCH FULL vs MATCH SIMPLE in foreign key constraints
- CONSTRAINT to check values from a remotely related table (via join etc.)
- Disable all constraints and table checks while restoring a dump
- Enforcing constraints “two tables away”