【问题标题】:How to have a foreign key pointing to two primary keys?如何让外键指向两个主键?
【发布时间】:2017-07-05 06:22:06
【问题描述】:

我正在尝试简化数据库结构,我有两个表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 之间设置另一个表来实现这一点,我愿意接受建议,但我仍然想知道如何让一个外键引用两个主键。

【问题讨论】:

  • team_statistics.team_id 的所有值都应该在matches.team_idmatches.team_id1 之间——对吧?..
  • team_statistics 中的每一行都应该是一个团队,如果team_statistics.team_id 在匹配表中,那么你怎么说某个统计数据属于一个特定团队,另一个 team_id 会有被设为空,总体而言,它不太适合表格
  • 请阅读我的回答并提供您问题中的示例数据
  • 我正在寻找一种用于可视化数据库模式表示的工具。您的架构看起来很不错。你用的是什么工具?抱歉,有一个不相关的问题。

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


【解决方案1】:

FK 约束规则

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

“我还是想知道如何让一个外键引用两个主键。”

这是不可能的。

  • 一个FOREIGN KEY约束只能指向一个表,每个表只能有一个@ 987654322@约束。

  • 或者,您可以在同一列上设置 多个 FOREIGN KEY 约束,分别引用一个(不同)表的 一个 PRIMARY KEY。 (很少有用。)

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

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

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

你问什么

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

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

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

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);

甚至可能对某些设置有意义,但对您的设置不适用。

你可能需要什么

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

team_statistics 中的(match_id, team_id) 应该是引用表matches 中的或者 (match_id, team_id) (match_id, team_id1) 的外键。

这对于 FK 约束和只有两个表是不可能的。您可以使用伪造的IMMUTABLE 函数滥用CHECK 约束并使其变为NOT VALID。请参阅此答案中的“使用 CHECK 约束更便宜”一章:

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

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)
);

home 标记比赛的主队,但通过包含在 PK 中,也限制为每场比赛最多两支球队。 (PK 列被隐式定义为NOT NULL。)

(team_id, match_id) 上的可选 UNIQUE 约束可防止团队与自己对抗。通过使用索引列的倒序(与执行规则无关),这也提供了与 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;

基本建议:

【讨论】:

  • 哇!这是一个很好的答案!我需要时间来解决这个问题,因为我是初学者。 BRB
  • 关于您的观点:“或者您可以在同一列上有多个 FOREIGN KEY 约束,每个列引用一个(不同)表的一个主键。(很少有用。)”,什么时候会有用吗,因为你说它很少有用?这种方法在我看来是一种解决需求的方法。
【解决方案2】:

我认为您需要在某个地方跟踪团队。比如:

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

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

您也可以从 team_statistics 为比赛创建多个外键,但如果这样做,您将无法获取统计数据,直到一支球队同时处于比赛的一侧和另一侧。

【讨论】:

    【解决方案3】:

    如果我理解你的概念,这里有一个例子:

    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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-17
      • 1970-01-01
      • 1970-01-01
      • 2017-03-09
      相关资源
      最近更新 更多