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

时间:2017-02-16 08:46:59

标签: sql postgresql database-design foreign-keys many-to-many

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

enter image description here

team_statistics表中team_statistics.team_id应该是引用matches.teams_idmatches.teams_id1的外键,同样team_statistics.group_id应该是引用{的外键{1}}和matches.groups_id

如何在PostgreSQL中执行此操作?

如果通过在matches.groups_id1matches之间设置另一个表格来实现此目的,还有其他方法我可以提出建议,但我仍然想知道如何让一个外键引用两个主要的密钥。

3 个答案:

答案 0 :(得分:7)

FK约束规则

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

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

那是不可能的。

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

  • 或者您可以在引用一个 FOREIGN KEY(相同)的相同列上有多个 PRIMARY KEY约束每个表。 (非常有用。)

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

  

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

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

你问什么

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

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

(team_id, team_id1)matches的组合需要定义UNIQUEteam_statistics.team_id中的值仅限于表team = 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);

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

你可能需要什么

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

(match_id, team_id)中的

team_statistics应该是引用 (match_id, team_id) {{1}的外键在表(match_id, team_id1)中。

对于FK约束和两个表,这是不可能的。您可以滥用matches约束与假CHECK函数并将其设为IMMUTABLE。请参阅"使用CHECK约束更便宜"在这个答案:

但这种高级技巧和不太可靠。这不是我的建议,所以我不打算详细说明。我建议以有用的方式 normalize 您的架构,例如:

NOT VALID

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) ); 标志着比赛的主队,但是,通过包含在PK中,还限制每场比赛最多两支球队。 (PK列是隐式定义的home。)

NOT NULL上的可选UNIQUE约束会阻止团队对抗自己。通过使用索引列的反转序列(与强制执行规则无关),这也提供了与PK互补的索引,这通常也是有用的。参见:

可以添加单独的(team_id, match_id),但现在只是match_team_statistics的可选1:1扩展名。或者,只需将列添加到match_team

我可能会为典型的展示添加观看次数,例如:

match_team

基本建议:

答案 1 :(得分:0)

我认为您需要在某个地方跟踪团队。类似的东西:

CREATE TABLE team_groups (
    team_id int,
    group_id int,
    primary key (team_id, group_id)
);

然后你需要匹配表有两个外键对此。那么你的统计表也应该引用它。

你可以从team_statistics创建多个外键来匹配,但如果这样做,你将无法获取统计数据,直到一个团队同时在一方和另一方匹配。

答案 2 :(得分:-1)

如果我的概念正确,这是一个例子:

t=# create table matches(team_id int unique,team_id1 int unique);                             CREATE TABLE
t=# insert into matches values (0,0),(1,1),(2,3);                                             INSERT 0 3
t=# create table team_statistics (team_id int);                                               ERROR:  relation "team_statistics" already exists
t=# drop table team_statistics;
DROP TABLE
t=# drop table matches cascade;
DROP TABLE
t=# create table matches(team_id int unique,team_id1 int unique);
CREATE TABLE
t=# insert into matches values (0,0),(1,1),(2,3);
INSERT 0 3
t=# create table team_statistics (team_id int);
CREATE TABLE
t=# alter table team_statistics add constraint fk1 foreign key (team_id) references matches(team_id);
ALTER TABLE
t=# alter table team_statistics add constraint fk2 foreign key (team_id) references matches(team_id1);
ALTER TABLE
t=# insert into team_statistics values (0);
INSERT 0 1
t=# insert into team_statistics values (1);
INSERT 0 1
t=# insert into team_statistics values (2);
ERROR:  insert or update on table "team_statistics" violates foreign key constraint "fk2"
DETAIL:  Key (team_id)=(2) is not present in table "matches".
t=# insert into team_statistics values (3);
ERROR:  insert or update on table "team_statistics" violates foreign key constraint "fk1"
DETAIL:  Key (team_id)=(3) is not present in table "matches".
t=# select * from team_statistics;
 team_id
---------
       0
       1
(2 rows)

t=# select * from matches;
 team_id | team_id1
---------+----------
       0 |        0
       1 |        1
       2 |        3
(3 rows)