我如何最好地设计一个数据库,其中我有一个玩家表(主键 player_id ),我想将其配对成两个一组,以便数据库可以强制执行每个团队的约束由两个玩家组成,每个玩家最多只能在一个团队中?
我可以想到两种解决方案,但我都不太高兴。
一种可能性是有两列 player1_id 和 player2_id ,这些唯一外键指向 player_id 播放器表中的列。需要进一步检查,以便没有玩家同时参加一支球队的球员1和第二支球队的球员2。
我想到的另一种可能性是将播放器表和团队表连接到团队成员资格表,该表具有 player_id 列的唯一外键在播放器表中,第二个外键指向团队表的主键。在这里必须添加一个检查,每个团队只有两个成员。
是否有更好的设计简化了约束的检查?
如果重要:我正在使用的数据库是PostgreSQL 8.4,我更喜欢强大的rule system触发器。
编辑:基于AlexKuznetsov答案的解决方案
我觉得这对我来说并不完美,但我比以前更喜欢它。我修改了Alex的解决方案,因为我不想从玩家到团队获得外键,因为有一个应用程序阶段玩家可以注册。
create table TeamMemberships(
player_id int not null unique references Players(player_id),
team_id int not null references Teams(team_id),
NumberInTeam int not null check(NumberInTeam in (0,1)),
OtherNumberInTeam int not null, -- check(OtherNumberInTeam in (0,1)) is implied
check(NumberInTeam + OtherNumberInTeam = 1)
foreign key (team_id, OtherNumberInTeam) references TeamMemberships(team_id, NumberInTeam),
primary key (team_id, NumberInTeam)
);
此定义确保团队成员资格成对(并将成对插入)。现在玩家最多只能有一支球队,而球队可以拥有0名或2名球员。为确保每个团队都有成员,我可以在团队表中添加指向其两个成员资格中的任何一个的外键。但是像Erwin一样,我不是延迟约束检查的粉丝。任何想法如何改善这个?还是有一种完全不同的,更好的方法?
PS:这些方法也适用于拥有n> 2名玩家的球队。只需要通过NextNumberInTeam用Number(即约束)NumberInTeam + 1 mod n替换OtherNumberInTeam。
答案 0 :(得分:1)
我不知道这是否适用于Postgress,但这是一个SQL Server解决方案:
CREATE TABLE dbo.Teams(TeamID INT NOT NULL PRIMARY KEY);
GO
CREATE TABLE dbo.Players(PlayerID INT NOT NULL PRIMARY KEY,
TeamID INT NOT NULL FOREIGN KEY REFERENCES dbo.Teams(TeamID),
NumberInTeam INT NOT NULL CHECK(NumberInTeam IN (1,2)),
TeamMateID INT NOT NULL,
TeamMatesNumberInTeam INT NOT NULL,
-- if NumberInTeam=1 then TeamMatesNumberInTeam must be 2
-- and vise versa
CHECK(NumberInTeam+TeamMatesNumberInTeam = 3),
UNIQUE(TeamID, NumberInTeam),
UNIQUE(PlayerID, TeamID, NumberInTeam),
FOREIGN KEY(TeamMateID, TeamID, TeamMatesNumberInTeam)
REFERENCES dbo.Players(PlayerID, TeamID, NumberInTeam)
);
INSERT INTO dbo.Teams(TeamID) SELECT 1 UNION ALL SELECT 2;
GO
- 您只能完整插入玩家
INSERT INTO dbo.Players(PlayerID, TeamID, NumberInTeam, TeamMateID, TeamMatesNumberInTeam)
SELECT 1,1,1,2,2 UNION ALL
SELECT 2,1,2,1,1;
您可以尝试插入单个播放器,或从团队中删除播放器,或者每个团队插入两个以上的播放器 - 由于一组完整的约束,所有播放器都会失败。
注意:SQL Server中的做法是明确命名所有约束。我没有说明我的约束,以防万一与Postgres不兼容。
答案 1 :(得分:0)
这听起来像是在用户界面中比在数据库中更容易实施的东西。
在向球队添加球员时,不允许添加超过2名球员,并且只允许添加不在球队中的球员。
答案 2 :(得分:0)
如果可能需要更换为有三名成员的团队,第一种解决方案是有限的。
我喜欢teamMemebrs表的想法,但为了强制执行约束,您将无法一次只插入一条记录。所有插入都必须是两个插件。此外,当A队中的球员A ia和你希望他被转移到B队时你会做什么,你会变得很复杂。现在你必须找到一个人转移到A队,然后将他和他的伙伴加入到团队中乙
创建和填充团队可能会更好,但如果他们有两个成员并且每个成员只在一个团队中,那么他们只能活跃吗?至少那时你可以一次做一个更改。 SP你可以将A组从A队移到B队,让A队无人值守,直到找到另一个人加入。
答案 3 :(得分:0)
在我看来,使用player1-id player2_id设计,您可以在向团队表单次插入后检查所有约束。
在团队+团队成员资格设计中,除了你必须设置的所有触发/规则之外,你还会进入延迟约束检查的丑陋。
顺便提一下:诸如SIRA_PRISE之类的系统的构建具有明确的目的,即纯粹以声明方式处理这些类型的约束执行问题,即没有任何触发器麻烦或涉及任何其他形式的编程。你可能会感兴趣。