【问题标题】:How to create a Postgres table with unique combined primary key?如何创建具有唯一组合主键的 Postgres 表?
【发布时间】:2016-02-15 18:37:43
【问题描述】:

我在 Postgres 数据库中有两个名为 players 和 matches 的表,如下所示:

CREATE TABLE players (
    name text NOT NULL,
    id serial PRIMARY KEY
);

CREATE TABLE matches (
    winner int REFERENCES players (id),
    loser int REFERENCES players (id),
    -- to prevent rematch btw players
    CONSTRAINT unique_matches
    PRIMARY KEY (winner, loser)
);

如何确保只有 (winner, loser) 或 (loser, winner) 的唯一组合用于 matches 主键,以便 matches 表不允许插入:

INSERT INTO matches VALUES (2, 1);

如果它已经有一行包含VALUES (1, 2) 像:

 winner | loser
--------+-------
      1 |     2

目标是避免相同玩家之间的比赛进入。

【问题讨论】:

    标签: sql postgresql database-design ddl unique-constraint


    【解决方案1】:

    创建唯一索引:

    CREATE UNIQUE INDEX matches_uni_idx ON matches
       (greatest(winner, loser), least(winner, loser));
    

    不能是UNIQUE or PRIMARY KEY constraint,因为它们仅适用于列,不适用于表达式。

    您可以添加一个 serial 列作为 PK,但只有两个整数列,您的原始 PK 也非常有效(请参阅 cmets)。它会自动生成两列NOT NULL。 (否则,添加NOT NULL 约束。)

    您还可以添加CHECK 约束来排除玩家与自己对战:

    CHECK (winner <> loser)
    

    提示:要搜索一对 ID(您不知道谁赢了),请在查询中构建相同的表达式,然后将使用索引:

    SELECT * FROM matches
    WHERE  greatest(winner, loser) = 3  -- the greater value, obviously
    AND    least(winner, loser) = 1;
    

    如果您处理未知参数并且您不知道提前哪个更大:

    WITH input AS (SELECT $id1 AS _id1, $id2 AS _id2)  -- input once
    SELECT * FROM matches, input
    WHERE  greatest(winner, loser) = greatest(_id1, _id2)
    AND    least(winner, loser) = least(_id1, _id2);
    

    CTE 包装器只是为了方便输入参数一次,在某些情况下不是必需的。

    【讨论】:

    • 谢谢@Erwin。它就像一个魅力;)但是,您是否有理由建议使用serial 列作为PK,而不是我以前的方式(PRIMARY KEY (winner, loser))?再次感谢:)
    • @BabakK:你说得对,PRIMARY KEY (winner, loser) 会这样工作。并且有两个整数列,它也非常有效。如果您对表有 FK 引用,则使用单列代理 PK 可能更简单。
    【解决方案2】:

    Postgres 不支持对表达式的约束,所以我想不出一种直接的方式来将此要求表达为约束。但是,您可以做的一件事是更改表的结构,使其具有两列用于比赛的玩家(主键),一个确保 player1 始终具有两者中较小的 id 的约束和一个用于指示的附加列获胜者:

    CREATE TABLE matches (
        p1 int REFERENCES players (id),
        p2 int REFERENCES players (id),
        p1winner boolean,
    
        CONSTRAINT matches_pk PRIMARY KEY (p1, p2),
        CONSTRAINT matches_players_order CHECK (p1 < p2)
    );
    

    【讨论】:

      猜你喜欢
      • 2021-04-10
      • 2011-03-26
      • 2012-03-13
      • 1970-01-01
      • 1970-01-01
      • 2014-01-27
      • 1970-01-01
      • 2012-09-07
      • 1970-01-01
      相关资源
      最近更新 更多