Crie um índice exclusivo:
CREATE UNIQUE INDEX matches_uni_idx ON matches
(greatest(winner, loser), least(winner, loser));
Não pode ser um
UNIQUE
ou PRIMARY KEY
restrição, já que eles só funcionam com colunas, não com expressões. Você pode adicionar um
serial
column para servir como PK, mas com apenas duas colunas inteiras, seu PK original também é muito eficiente (veja comentários). E torna ambas as colunas NOT NULL
automaticamente. (Caso contrário, adicione NOT NULL
restrições.) Você também pode adicionar um
CHECK
restrição para descartar jogadores jogando contra eles mesmos:CHECK (winner <> loser)
Dica:para pesquisar um par de IDs (onde você não sabe quem ganhou), construa as mesmas expressões em sua consulta e o índice será usado:
SELECT * FROM matches
WHERE greatest(winner, loser) = 3 -- the greater value, obviously
AND least(winner, loser) = 1;
Se você lida com parâmetros desconhecidos e não sabe qual é maior antecipadamente:
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);
O wrapper CTE é apenas por conveniência para inserir parâmetros apenas uma vez e não é necessário em alguns contextos.