如何将外键指向两个主键?

pin*_* af 2 sql postgresql database-design many-to-many foreign-keys

我正在尝试简化数据库结构,我有两个表matchesteam_statistics:

在此输入图像描述

在这里,在team_statistics表中team_statistics.team_id应该是一个外键引用matches.teams_idmatches.teams_id1,同样team_statistics.group_id应该是一个外键引用matches.groups_idmatches.groups_id1

如何在PostgreSQL中执行此操作?

如果存在具有与另一个表这样做的其他方式matchesteam_statistics我开放的建议,但我仍想知道如何有一个外键引用两个主键.

Erw*_*ter 9

FK约束的规则

要回答标题和文字末尾的问题:

"我仍然想知道如何让一个外键引用两个主键."

这不可能.

  • 一个FOREIGN KEY约束只能指向一个表,每个表只能有一个 PRIMARY KEY约束.

  • 或者,您可以对引用每个(不同)表之一的相同列具有多个 FOREIGN KEY约束.(很少用.) PRIMARY KEY

但是,单个PK或FK 可以跨越多个列.
并且FK可以引用目标中任何明确定义的唯一(一组)列,而不仅仅是PK.手册:

外键必须引用作为主键或形成唯一约束的列.

多列PK或UNIQUE约束只能由具有匹配列类型的多列FK约束引用.

你问什么

由于不允许在a UNIQUEPRIMARY KEY约束的列列表中多次使用同一列,因此a 的目标列表FOREIGN KEY也不能多次使用同一列.但是没有什么可以阻止我们在列表中多次使用同一列.这有可能实现你所要求的(但可能并不意味着):

"在team_statistics表中team_statistics.team_id应该是引用的外键matches.team_idmatches.team_id1"

需要定义(team_id, team_id1)表中的组合.值将仅限于表中作为逻辑结果的情况:matchesUNIQUEteam_statistics.team_idteam = team1matches

ALTER TABLE matches
ADD constraint matches_teams_groups_uni UNIQUE (team_id, team_id1);

ALTER TABLE team_statistics
  ADD constraint team_statistics_team_group fkey
  FOREIGN KEY (team_id, team_id)  -- same column twice!
  REFERENCES matches(team_id, team_id1);
Run Code Online (Sandbox Code Playgroud)

甚至可能对某些设置有意义,但不是你的设置.

你可能需要什么

我有根据的猜测是你想要这样的东西:

(match_id, team_id)in table team_statistics应该是引用其中一个 (match_id, team_id) (match_id, team_id1)在表中的外键matches.

这对于FK约束和两个表是不可能的.你可以CHECK使用假IMMUTABLE函数滥用约束并制作它NOT VALID.请参阅本答案中的"带有CHECK约束的更便宜"一章:

但那是先进的技巧,而且不太可靠.这不是我的建议,所以我不打算详细说明.我建议以有用的方式规范化您的架构,例如:

CREATE TABLE team (team_id serial PRIMARY KEY
                 , team text NOT NULL UNIQUE);     -- add more attributes for team

CREATE TABLE match (match_id serial PRIMARY KEY);  -- add more attributes for match

CREATE TABLE match_team (
   match_id  int  REFERENCES match  -- short notation for FK
 , team_id   int  REFERENCES team
 , home boolean                     -- TRUE for home team, FALSE for away team
 , innings_score int
 -- more attributes of your original "team_statistics"
 , PRIMARY KEY (match_id, team_id, home)  -- !!! (1st column = match_id)
 , UNIQUE (team_id, match_id)             -- optional, (1st column = team_id)
);
Run Code Online (Sandbox Code Playgroud)

home标志着比赛的主队,但是,通过包含在PK中,每场比赛还限制最多两支球队.(PK列是NOT NULL隐式定义的.)

可选UNIQUE约束(team_id, match_id)可防止团队对抗自己.通过使用索引列的反转序列(与强制执行规则无关),这也提供了与PK互补的索引,这通常也是有用的.看到:

可以添加一个单独的match_team_statistics,但这只是match_team现在的可选1:1扩展.或者只需添加列match_team.

我可能会为典型的显示添加视图,例如:

CREATE VIEW match_result AS
SELECT m.match_id
     , concat_ws(' : ', t1.team, t2.team) AS home_vs_away_team
     , concat_ws(' : ', mt1.innings_score, mt2.innings_score) AS result
FROM   match           m
LEFT   JOIN match_team mt1 ON mt1.match_id = m.match_id AND mt1.home
LEFT   JOIN team       t1  ON t1.team_id = mt1.team_id
LEFT   JOIN match_team mt2 ON mt2.match_id = m.match_id AND NOT mt2.home
LEFT   JOIN team       t2  ON t2.team_id = mt2.team_id;
Run Code Online (Sandbox Code Playgroud)

基本建议: