Regras para restrições FK
Para responder à pergunta no título e no final do seu texto:
"Ainda gostaria de saber como ter uma chave estrangeira referenciando duas chaves primárias."
Isso é impossível.
-
UmaFOREIGN KEY
restrição só pode apontar para um tabela e cada tabela pode ter apenas umaCHAVE PRIMÁRIA
restrição.
-
Ou você pode ter váriosCHAVE ESTRANGEIRA
restrições na(s) mesma(s) coluna(s) referenciando umaCHAVE PRIMÁRIA
de uma mesa (diferente) cada. (Raramente útil.)
No entanto , um único PK ou FK pode abrange várias colunas.
E um FK pode referenciar qualquer coluna(s) exclusiva(s) definida(s) explicitamente no destino, não apenas o PK. O manual:
Um PK de várias colunas ou
ÚNICO
A restrição só pode ser referenciada por uma restrição FK de várias colunas com tipos de coluna correspondentes. O que você pergunta
Como não é permitido usar a mesma coluna mais de uma vez na lista de colunas de um
UNIQUE
ou CHAVE PRIMÁRIA
restrição, a lista de destino de uma FOREIGN KEY
também não pode usar a mesma coluna mais de uma vez. Mas não há nada que nos impeça de usar a mesma coluna mais de uma vez na fonte Lista. Aqui reside o potencial de implementar o que você está pedindo (mas provavelmente não pretendia):"Na
team_statistics
tabela o team_statistics.team_id
deve ser uma chave estrangeira que faça referência a matches.team_id
e matches.team_id1
" A combinação de
(team_id, team_id1)
na tabela corresponde
precisaria ser definido UNIQUE
. Valores em team_statistics.team_id
seria restrito a casos com team =team1
na tabela corresponde
como consequência lógica:ALTER TABLE matches
ADD constraint matches_teams_groups_uni UNIQUE (team_id, team_id1);
ALTER TABLE team_statistics
ADD constraint team_statistics_team_group fkey
FOREIGN KEY (team_id, team_id) -- same column twice!
REFERENCES matches(team_id, team_id1);
Pode até fazer sentido para certas configurações, mas não para a sua.
O que você provavelmente precisa
Meu palpite é que você quer algo assim:
(match_id, team_id)
na tabela team_statistics
deve ser uma chave estrangeira que faça referência a ou (match_id, team_id)
ou (match_id, team_id1)
na tabela corresponde
. E isso não é possível com restrições FK e apenas duas tabelas. Você poderia abusar de um
CHECK
restrição com um falso IMMUTABLE
função e torná-lo NOT VALID
. Consulte o capítulo "Mais barato com uma restrição CHECK" nesta resposta:Mas isso é um truque avançado e menos confiável. Não é minha sugestão aqui, então não vou elaborar. Sugiro normalizar seu esquema de uma maneira útil, como:
CREATE TABLE team (team_id serial PRIMARY KEY
, team text NOT NULL UNIQUE); -- add more attributes for team
CREATE TABLE match (match_id serial PRIMARY KEY); -- add more attributes for match
CREATE TABLE match_team (
match_id int REFERENCES match -- short notation for FK
, team_id int REFERENCES team
, home boolean -- TRUE for home team, FALSE for away team
, innings_score int
-- more attributes of your original "team_statistics"
, PRIMARY KEY (match_id, team_id, home) -- !!! (1st column = match_id)
, UNIQUE (team_id, match_id) -- optional, (1st column = team_id)
);
casa
marca o time da casa da partida, mas, por inclusão no PK, também restringe a máximo de duas equipes por partida . (As colunas PK são definidas como NOT NULL
implicitamente.) O opcional
UNIQUE
restrição em (team_id, match_id)
impede que as equipes joguem contra si mesmas. Ao usar a sequência invertida de colunas de índice (irrelevante para impor a regra), isso também fornece um índice complementar ao PK, que normalmente também é útil. Ver:Você poderia adicione um
match_team_statistics
separado , mas isso seria apenas uma extensão 1:1 opcional para match_team
agora. Como alternativa, basta adicionar colunas a match_team
. Posso adicionar visualizações para telas típicas, como:
CREATE VIEW match_result AS
SELECT m.match_id
, concat_ws(' : ', t1.team, t2.team) AS home_vs_away_team
, concat_ws(' : ', mt1.innings_score, mt2.innings_score) AS result
FROM match m
LEFT JOIN match_team mt1 ON mt1.match_id = m.match_id AND mt1.home
LEFT JOIN team t1 ON t1.team_id = mt1.team_id
LEFT JOIN match_team mt2 ON mt2.match_id = m.match_id AND NOT mt2.home
LEFT JOIN team t2 ON t2.team_id = mt2.team_id;
Conselho básico: