Sql 如何创建具有唯一组合主键的Postgres表?

Sql 如何创建具有唯一组合主键的Postgres表?,sql,postgresql,database-design,ddl,unique-constraint,Sql,Postgresql,Database Design,Ddl,Unique Constraint,我在Postgres DB中有两个名为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 bt

我在Postgres DB中有两个名为
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)
);
如何确保只有
(赢家,输家)
(输家,输家)
的唯一组合用于
匹配
主键,以便
匹配
表不允许插入:

INSERT INTO matches VALUES (2, 1);
如果它已经有一行包含
值(1,2)
如:

 winner | loser
--------+-------
      1 |     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)
);
创建表匹配(
p1 int参考球员(id),
p2 int参考球员(id),
p1是一个布尔值,
约束匹配主键(p1,p2),
约束匹配\u玩家\u顺序检查(p1
创建唯一索引:

CREATE UNIQUE INDEX matches_uni_idx ON matches
   (greatest(winner, loser), least(winner, loser));
不能是,因为这些只能处理列,不能处理表达式

您可以添加一个
serial
列作为主键,但是只有两个整数列,您的原始主键也非常有效(请参见注释)。并且它会自动使两列
都不为NULL
。(否则,添加
notnull
约束。)

您还可以添加
检查
约束,以排除与自己对抗的玩家:

CHECK (winner <> loser)
如果您处理未知参数,但不知道哪一个参数更大:

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(赢家,输家)
)?再次感谢:)@BabakK:您是对的,
PRIMARY KEY(赢家,输家)
将以这种方式工作。对于两个整数列,它也非常高效。如果对表有FK引用,则使用单列代理PK可能更简单。
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);