我正在尝试简化数据库结构,我有两个表matches
和team_statistics
:
team_statistics
表中team_statistics.team_id
应该是引用matches.teams_id
和matches.teams_id1
的外键,同样team_statistics.group_id
应该是引用{的外键{1}}和matches.groups_id
如何在PostgreSQL中执行此操作?
如果通过在matches.groups_id1
和matches
之间设置另一个表格来实现此目的,还有其他方法我可以提出建议,但我仍然想知道如何让一个外键引用两个主要的密钥。
答案 0 :(得分:7)
要回答标题和文字末尾的问题:
"我仍然想知道如何让一个外键引用两个主键。"
那是不可能的。
FOREIGN KEY
约束只能指向 一个 表,每个表只能 一个 PRIMARY KEY
约束。
或者您可以在引用一个 FOREIGN KEY
(相同)的相同列上有多个 PRIMARY KEY
约束每个表。 (非常有用。)
但,单个PK或FK 可以跨越多列。
并且FK可以引用目标中任何明确定义的唯一(一组)列,而不仅仅是PK。 The manual:
外键必须引用作为主键或形成唯一约束的列。
多列PK或UNIQUE
约束只能由具有匹配列类型的多列FK约束引用。
由于不允许在UNIQUE
或PRIMARY KEY
约束的列列表中多次使用同一列,因此FOREIGN KEY
的目标列表也不能使用同一列不止一次。但是,没有什么可以阻止我们在 源 列表中多次使用同一列。这有可能实现你所要求的(但可能并不意味着):
"在team_statistics
表中,team_statistics.team_id
应该是引用matches.team_id
和matches.team_id1
" 的外键
表(team_id, team_id1)
中matches
的组合需要定义UNIQUE
。 team_statistics.team_id
中的值仅限于表team = team1
中matches
的情况作为逻辑结果:
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)