FK 约束规则
要回答标题和文本末尾的问题:
“我还是想知道如何让一个外键引用两个主键。”
这是不可能的。
但是,单个 PK 或 FK可以跨越多个列。
FK 可以引用目标中任何明确定义的唯一(组)列,而不仅仅是 PK。 The manual:
外键必须引用作为主键或形成唯一约束的列。
多列 PK 或 UNIQUE 约束只能由具有匹配列类型的多列 FK 约束引用。
你问什么
由于在UNIQUE 或PRIMARY KEY 约束的列列表中不允许多次使用同一列,所以FOREIGN KEY 的目标列表也不能多次使用同一列。但是没有什么可以阻止我们在 source 列表中多次使用同一列。这里有可能实现您所要求的(但可能并非有意):
“在team_statistics 表中,team_statistics.team_id 应该是引用matches.team_id 和matches.team_id1 的外键”
表matches 中(team_id, team_id1) 的组合需要定义UNIQUE。 team_statistics.team_id 中的值将仅限于表 matches 中 team = 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;
基本建议: