如何在PostgreSQL中正确设计VS匹配表?

时间:2011-10-18 06:38:59

标签: sql postgresql database-design

我已经考虑过这个问题了,我还没有想出更好的东西。那么让我来描述我的问题,我目前的解决方案,以及我想要改进的地方。我也有一些顾虑,比如我的设计是否实际正常化。

我正在创建一个数据库,我希望为比赛存储VS比赛信息。为简单起见,我们假装他们是国际象棋比赛。 1V1。我目前的设计如下:

CREATE TABLE matches(
  match_id bigserial PRIMARY KEY,
  tournament_id int NOT NULL,
  step int NOT NULL,
  winner match_winner,
  (etc. etc.)
  UNIQUE(match_id, tournament_id, step), -- Actual primary key
  FOREIGN KEY (tournament_id) references tournaments(tournament_id)
    ON DELETE RESTRICT
    ON UPDATE CASCADE
);

CREATE TABLE match_players(
  match_id bigint NOT NULL,
  tournament_id int NOT NULL,
  step int NOT NULL,
  player_id int NOT NULL,
  first boolean NOT NULL,
  PRIMARY KEY (match_id, tournament_id, step, player_id),
  UNIQUE (tournament_id, step, player_id),
  foreign key (match_id, tournament_id, step) -- keep em together
        references matches(match_id, tournament_id, step)
    ON DELETE RESTRICT
    ON UPDATE CASCADE,
  foreign key (player_id) references accounts(player_id)
    ON DELETE RESTRICT
    ON UPDATE CASCADE
);

-- Partial index, ensure no more than one "first" player exists per match
CREATE UNIQUE INDEX idx_match_players_primary
    ON match_players
    USING btree
    (match_id, tournament_id, step)
    WHERE first=true;

-- Also ensure that no more than one "not-first" player exists per match
CREATE UNIQUE INDEX idx_match_players_not_primary
    ON match_players
    USING btree
    (match_id, tournament_id, step)
    WHERE first=false;

为了得到实际的vs匹配,我可以简单地将match_players加入到自身中(在mp1.match_id = mp2.match_id,mp1.first = true和mp2.first = false,其中mp1和mp2是匹配的两个实例)。部分唯一索引确保最多可以添加两个玩家。

数据库已经这样规范化,因为玩家是无序的。如同,A vs B与B vs A相同。我已将“first”布尔值添加到匹配中,以便可以始终显示A vs B. (我想我可以简化它,以便mp1.player_id< mp2.player_id ......但“第一个”布尔值似乎有效。)

tournament_id和step在第二个表中重复,因为在该表的Unique索引上需要它们...以确保玩家每个步骤只有一个匹配。

这是我的主要问题:

  1. 目前可能在第一个表中有孤立的行 (火柴)。一场比赛应该有两名球员。在 特别是,如果匹配表中存在匹配,则可以 在match_players表中没有匹配它的行。有没有 确保匹配ALWAYS有两个相关行的方法 matches_players?用“第一”方法,我绝对有限 每场比赛的球员数量少于2 ...所以搞清楚 确保最少2名球员的方法可以解决问题。
  2. 以下是我的一个问题:

    • 由于孤立的行仍然存在,是否还有其他数据异常 在这个设计中会出现什么?我有点不舒服 match_players中的复合(三重)主键,但我认为 复合foreign_key要求涵盖了我的表格。

    感谢任何可以帮助我的人。这是迄今为止我能做的最好的事情。我想如果我解决孤立的行问题,那么这个设计将是完美的。我想我可以设置一个cron工作来清除孤立的行,但我想知道在解决这个问题之前是否存在更清洁的设计。

    我认为检查约束中的子查询可以解决问题,但是,我不认为PostgreSQL实际上支持该功能。

1 个答案:

答案 0 :(得分:2)

这就是我所说的“前瞻性问题”,即您可能遇到有关依赖尚未插入的行的数据约束的问题。整个事务具有各行不需要的要求。大多数数据库提供的解决此问题的工具很少。幸运的是,PostgreSQL为您提供了一些选择。

使用TOAST

的非规范化“输入缓冲区”

第一种方法是在匹配类型为match_player[]的匹配项中添加一列。然后你会在比赛中存储一系列球员。这将使用触发器实现到match_player表。尽管在发展和预见​​角落案件方面存在严重的处罚。我确实认为这是一个可行的选择,但它不是一个理想的选择。这通过压平表来避免前瞻性约束。但是它只能存储第0步记录。一旦人们移动......必须通过仅插入match_players来完成。

约束触发器

第二种方法是创建一个触发器函数,该函数在每个语句中运行一次,作为在事务结束时执行的INITIALLY DEFERRED DEFERRABLE CONSTRAINT TRIGGER。这将从表中提取系统列以查找插入的行,然后检查以确保匹配发生在另一个表中。这可能是解决前瞻性约束问题的最佳通用方法。