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

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

我正在创建一个数据库,我希望为比赛存储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索引上需要它们…以确保玩家每个步骤只有一个匹配. 这是我的主要问题:
>目前可能在第一个表中有孤立的行
(火柴).一场比赛应该有两名球员.在
特别是,如果匹配表中存在匹配,则可以
在match_players表中没有匹配它的行.有没有
确保匹配ALWAYS有两个相关行的方法
matches_players?用“第一”方法,我绝对有限
每场比赛的球员数量少于2 …所以搞清楚
确保最少2名球员的方法可以解决问题.

这是我的一个担忧:

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

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

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

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

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

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

约束触发器

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

点赞