两列的唯一性
uniqueness in two columns
这是一个关于足球的简单数据集:一个 table team
和一个 table match
Table « soccer.team »
┌─────────┬─────────┬───────────────┐
│ Column │ Type │ Modifiers │
├─────────┼─────────┼───────────────┤
│ team_id │ integer │ not NULL │
│ name │ text │ not NULL │
│ code │ text │ │
└─────────┴─────────┴───────────────┘
Index:
"team_pkey" PRIMARY KEY, btree (team_id)
Referenced by:
TABLE "match" CONSTRAINT "match_foreign_team_id_fkey" FOREIGN KEY (foreign_team_id) REFERENCES team(team_id)
TABLE "match" CONSTRAINT "match_home_team_id_fkey" FOREIGN KEY (home_team_id) REFERENCES team(team_id)
Table « soccer.match »
┌────────────────────┬──────────┬───────────────┐
│ Column │ Type │ Modifiers │
├────────────────────┼──────────┼───────────────┤
│ matchday │ integer │ not NULL │
│ home_team_id │ integer │ not NULL │
│ foreign_team_id │ integer │ not NULL │
│ home_team_score │ smallint │ not NULL │
│ foreign_team_score │ smallint │ not NULL │
└────────────────────┴──────────┴───────────────┘
Check constraints:
"match_check" CHECK (home_team_id <> foreign_team_id)
"match_foreign_team_score_check" CHECK (foreign_team_score >= 0)
"match_home_team_score_check" CHECK (home_team_score >= 0)
Foreign keys:
"match_foreign_team_id_fkey" FOREIGN KEY (foreign_team_id) REFERENCES team(team_id)
"match_home_team_id_fkey" FOREIGN KEY (home_team_id) REFERENCES team(team_id)
每个比赛日每支球队最多只能打一场比赛,无论是接待外国球队还是外国球队。必须有 2 支球队,每场比赛只能有 2 支球队。
是否有一种设计可以确保一支球队每个比赛日最多比赛一次?
听起来像一个独特的约束符合要求:
ALTER TABLE match ADD UNIQUE (matchday, home_team_id);
ALTER TABLE match ADD UNIQUE (matchday, foreign_team_id);
如果一支球队可以是主队和外国球队,事情就变得更复杂了,你需要这些扩展:
CREATE EXTENSION intarray SCHEMA public; -- for the "gist__int_ops" opclass
CREATE EXTENSION btree_gist SCHEMA public; -- for the "gist_int4_ops" opclass
然后你可以创建一个排除约束:
ALTER TABLE soccer.match ADD EXCLUDE USING gist (
matchday gist_int4_ops WITH OPERATOR(pg_catalog.=),
(ARRAY[home_team_id, foreign_team_id]) gist__int_ops WITH OPERATOR(public.&&)
);
基本上,如果两行的 matchday
相等并且 home_team_id
和 foreign_team_id
组成的数组有共同的元素,则两行算作“相等”。
你可以在这里使用两位数比较技巧,例如:
create unique index ui on soccer.match (matchday, greatest(home_team_id,foreign_team_id), least(home_team_id,foreign_team_id))
如果您有如下所示的 table,您可以在 matchday
和 team_id
上放置一个唯一约束或 multi-column 主键:
team_match
table:
┌────────────────────┬──────────┬───────────────┐
│ Column │ Type │ Modifiers │
├────────────────────┼──────────┼───────────────┤
│ matchday │ integer │ not NULL │
│ team_id │ integer │ not NULL │
│ is_home_team │ integer │ not NULL │
│ match_id │ integer │ not NULL │
│ score │ integer │ not NULL │
└────────────────────┴──────────┴───────────────┘
match
table:
┌────────────────────┬──────────┬───────────────┐
│ Column │ Type │ Modifiers │
├────────────────────┼──────────┼───────────────┤
│ match_id │ integer │ not NULL │
└────────────────────┴──────────┴───────────────┘
一支球队在每个比赛日不能比赛超过一次,因此 table 列出比赛日和球队。结合比赛号码和主场或客场指示器,您将获得所需的所有数据。例如:
Table « soccer.match »
+-----------------------------------------------+
¦ Column ¦ Type ¦ Modifiers ¦
+--------------------+----------+---------------¦
¦ matchday ¦ integer ¦ not NULL ¦
¦ matchnum ¦ integer ¦ not NULL ¦
¦ team_id ¦ integer ¦ not NULL ¦
¦ teamtype ¦ varchar ¦ not NULL ¦
¦ score ¦ smallint ¦ not NULL ¦
+-----------------------------------------------+
Check constraints:
"match_teamtype_check" CHECK (teamtype IN ('HOME','AWAY'))
"match_score_check" CHECK (score >= 0)
Indexes:
"match_pkey" PRIMARY KEY, btree (matchday, match_no, team_id, teamtype)
"match_team_once_per_day" UNIQUE KEY, btree (matchday, team_id)
"match_two_teams_only" UNIQUE KEY, btree (matchday, matchnum, teamtype)
您的 table 设计看起来不错。但你是对的,它不能保证一个球队每个比赛日只打一次比赛。为此,您需要另一个 table。您可以做的是将另一个 table 添加到您现有的设计中并用触发器填充它。
Table « soccer.match_team »
+-----------------------------------------------+
¦ Column ¦ Type ¦ Modifiers ¦
+--------------------+----------+---------------¦
¦ matchday ¦ integer ¦ not NULL ¦
¦ team_id ¦ integer ¦ not NULL ¦
+-----------------------------------------------+
Index:
"match_team_pkey" PRIMARY KEY, btree (matchday, team_id)
现在编写一个触发器,每当写入 match
条记录时,它就会写入两条记录。 (如果您允许更新和删除,您可能还想编写一些代码。)
CREATE EXTENSION IF NOT EXISTS intarray;
CREATE EXTENSION IF NOT EXISTS btree_gist;
alter table match add exclude using gist
(
matchday with =,
(array[home_team_id,foreign_team_id]) with &&
);
这将防止插入具有匹配比赛日和重叠 [home_team_id、foreign_team_id] 数组的行。
这是一个关于足球的简单数据集:一个 table team
和一个 table match
Table « soccer.team »
┌─────────┬─────────┬───────────────┐
│ Column │ Type │ Modifiers │
├─────────┼─────────┼───────────────┤
│ team_id │ integer │ not NULL │
│ name │ text │ not NULL │
│ code │ text │ │
└─────────┴─────────┴───────────────┘
Index:
"team_pkey" PRIMARY KEY, btree (team_id)
Referenced by:
TABLE "match" CONSTRAINT "match_foreign_team_id_fkey" FOREIGN KEY (foreign_team_id) REFERENCES team(team_id)
TABLE "match" CONSTRAINT "match_home_team_id_fkey" FOREIGN KEY (home_team_id) REFERENCES team(team_id)
Table « soccer.match »
┌────────────────────┬──────────┬───────────────┐
│ Column │ Type │ Modifiers │
├────────────────────┼──────────┼───────────────┤
│ matchday │ integer │ not NULL │
│ home_team_id │ integer │ not NULL │
│ foreign_team_id │ integer │ not NULL │
│ home_team_score │ smallint │ not NULL │
│ foreign_team_score │ smallint │ not NULL │
└────────────────────┴──────────┴───────────────┘
Check constraints:
"match_check" CHECK (home_team_id <> foreign_team_id)
"match_foreign_team_score_check" CHECK (foreign_team_score >= 0)
"match_home_team_score_check" CHECK (home_team_score >= 0)
Foreign keys:
"match_foreign_team_id_fkey" FOREIGN KEY (foreign_team_id) REFERENCES team(team_id)
"match_home_team_id_fkey" FOREIGN KEY (home_team_id) REFERENCES team(team_id)
每个比赛日每支球队最多只能打一场比赛,无论是接待外国球队还是外国球队。必须有 2 支球队,每场比赛只能有 2 支球队。 是否有一种设计可以确保一支球队每个比赛日最多比赛一次?
听起来像一个独特的约束符合要求:
ALTER TABLE match ADD UNIQUE (matchday, home_team_id);
ALTER TABLE match ADD UNIQUE (matchday, foreign_team_id);
如果一支球队可以是主队和外国球队,事情就变得更复杂了,你需要这些扩展:
CREATE EXTENSION intarray SCHEMA public; -- for the "gist__int_ops" opclass
CREATE EXTENSION btree_gist SCHEMA public; -- for the "gist_int4_ops" opclass
然后你可以创建一个排除约束:
ALTER TABLE soccer.match ADD EXCLUDE USING gist (
matchday gist_int4_ops WITH OPERATOR(pg_catalog.=),
(ARRAY[home_team_id, foreign_team_id]) gist__int_ops WITH OPERATOR(public.&&)
);
基本上,如果两行的 matchday
相等并且 home_team_id
和 foreign_team_id
组成的数组有共同的元素,则两行算作“相等”。
你可以在这里使用两位数比较技巧,例如:
create unique index ui on soccer.match (matchday, greatest(home_team_id,foreign_team_id), least(home_team_id,foreign_team_id))
如果您有如下所示的 table,您可以在 matchday
和 team_id
上放置一个唯一约束或 multi-column 主键:
team_match
table:
┌────────────────────┬──────────┬───────────────┐
│ Column │ Type │ Modifiers │
├────────────────────┼──────────┼───────────────┤
│ matchday │ integer │ not NULL │
│ team_id │ integer │ not NULL │
│ is_home_team │ integer │ not NULL │
│ match_id │ integer │ not NULL │
│ score │ integer │ not NULL │
└────────────────────┴──────────┴───────────────┘
match
table:
┌────────────────────┬──────────┬───────────────┐
│ Column │ Type │ Modifiers │
├────────────────────┼──────────┼───────────────┤
│ match_id │ integer │ not NULL │
└────────────────────┴──────────┴───────────────┘
一支球队在每个比赛日不能比赛超过一次,因此 table 列出比赛日和球队。结合比赛号码和主场或客场指示器,您将获得所需的所有数据。例如:
Table « soccer.match » +-----------------------------------------------+ ¦ Column ¦ Type ¦ Modifiers ¦ +--------------------+----------+---------------¦ ¦ matchday ¦ integer ¦ not NULL ¦ ¦ matchnum ¦ integer ¦ not NULL ¦ ¦ team_id ¦ integer ¦ not NULL ¦ ¦ teamtype ¦ varchar ¦ not NULL ¦ ¦ score ¦ smallint ¦ not NULL ¦ +-----------------------------------------------+ Check constraints: "match_teamtype_check" CHECK (teamtype IN ('HOME','AWAY')) "match_score_check" CHECK (score >= 0) Indexes: "match_pkey" PRIMARY KEY, btree (matchday, match_no, team_id, teamtype) "match_team_once_per_day" UNIQUE KEY, btree (matchday, team_id) "match_two_teams_only" UNIQUE KEY, btree (matchday, matchnum, teamtype)
您的 table 设计看起来不错。但你是对的,它不能保证一个球队每个比赛日只打一次比赛。为此,您需要另一个 table。您可以做的是将另一个 table 添加到您现有的设计中并用触发器填充它。
Table « soccer.match_team » +-----------------------------------------------+ ¦ Column ¦ Type ¦ Modifiers ¦ +--------------------+----------+---------------¦ ¦ matchday ¦ integer ¦ not NULL ¦ ¦ team_id ¦ integer ¦ not NULL ¦ +-----------------------------------------------+ Index: "match_team_pkey" PRIMARY KEY, btree (matchday, team_id)
现在编写一个触发器,每当写入 match
条记录时,它就会写入两条记录。 (如果您允许更新和删除,您可能还想编写一些代码。)
CREATE EXTENSION IF NOT EXISTS intarray;
CREATE EXTENSION IF NOT EXISTS btree_gist;
alter table match add exclude using gist
(
matchday with =,
(array[home_team_id,foreign_team_id]) with &&
);
这将防止插入具有匹配比赛日和重叠 [home_team_id、foreign_team_id] 数组的行。