2015-11-14 75 views
4

我有两个名为表players在一个Postgres数据库& matches如下:如何用独特的组合主键创建postgres表?

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表赢“T允许的插入:

INSERT INTO matches VALUES (2, 1); 

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

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

目标是避免输入相同球员之间的比赛。

回答

4

创建唯一索引:

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

不能是UNIQUE or PRIMARY KEY constraint,因为通过列,不表现那些唯一的工作。

您可能会添加一个serial列作为PK,但只有两个整数列,您的原始PK也非常有效(请参阅注释)。它会自动生成两列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包装只是为了方便输入一次参数,在某些情况下不需要。

+0

谢谢@Erwin。它的工作就像一个魅力;)然而,是否有一个原因,你建议使用“串行”列作为PK,而不是我以前的方式('PRIMARY KEY(赢家,输家)')?再次感谢: ) –

+1

@BakakK:你说的对,'PRIMARY KEY(赢家,输家)'会这样工作。而且有两个整数列,它也非常有效。如果您有FK对表的引用,那么使用单列代理PK可能会更简单。 –

1

Postgres不支持对表达式的约束,所以我想不出将这个需求表达为约束的直接方式。但是你可以做的一件事是改变表格的结构,以便匹配的玩家有两列(主键),这是一个约束,确保玩家1总是有两个较小的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) 
);