2015-11-14 29 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) 
);