【问题标题】:Database design for constraint enforcing pairing约束执行配对的数据库设计
【发布时间】:2010-11-10 18:21:11
【问题描述】:

我如何最好地设计一个数据库,其中我有一个球员表(主键 player_id),我想将其配对成两个团队,以便数据库可以强制执行每个团队的约束由正好两个玩家组成,每个玩家最多一个队?

我可以想到两种解决方案,但我都不太满意。

一种可能性是有两列 player1_idplayer2_id唯一 指向 player_id 的外键玩家表中的列。需要进行额外检查,以确保没有球员同时是一队的 player1 和另一队的 player2。

我想到的另一种可能性是将玩家表和团队表连接到一个团队成员表,该表具有 player_id 列的唯一外键在 player 表中,第二个外键指向 team 表的主键。这里必须添加一个检查,每个团队只有两个成员。

是否有更好的设计来简化约束检查?

如果重要的话:我使用的数据库是 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 替换为 OtherNumberInTeam,其值(即约束)NumberInTeam+1 mod n。

【问题讨论】:

  • 有postgres物化视图吗?

标签: sql database-design postgresql


【解决方案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 不兼容。

【讨论】:

    【解决方案2】:

    这听起来在用户界面中比在数据库中更容易实施。

    在向队伍中添加玩家时,不允许添加超过 2 名玩家,并且只允许添加尚未加入队伍的玩家。

    【讨论】:

      【解决方案3】:

      第一个解决方案是有限的,如果可能需要更改为三个成员的团队。

      我喜欢 teamMemebrs 表的想法,但是为了强制执行您的约束,您将永远无法一次只插入一条记录。您所有的插入物都必须是一组两个。此外,当球员 A 在 A 队中并且你希望他被转移到 B 队时,你会变得很复杂。现在你必须找到一个人转移到 A 队,然后将他和他的搭档添加到队中B.

      创建和填充团队是否会更好,但只有当他们有两个成员并且每个成员只在一个团队中时才让他们活跃?至少,您可以一次进行一项更改。 SP,您可以将人员 A 从团队 A 移动到团队 B,并使团队 A 处于非活动状态,直到您找到另一个人来添加。

      【讨论】:

        【解决方案4】:

        在我看来,使用 player1-id player2_id 设计,您可以在一次插入到您的团队表后检查所有约束。

        在团队+团队成员设计中,除了必须设置的所有触发/规则内容之外,您会陷入延迟约束检查的丑陋之中。

        顺便说一句:像 SIRA_PRISE 这样的系统的构建目的是纯粹以声明方式精确处理这些类型的约束执行问题,即没有任何触发器的麻烦,或涉及任何其他形式的编程。你可能会感兴趣。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多