Reglas para restricciones FK
Para responder a la pregunta en el título y al final de su texto:
"Todavía me gustaría saber cómo hacer que una clave externa haga referencia a dos claves principales".
Eso es imposible.
-
Una
FOREIGN KEY
la restricción solo puede apuntar a uno tabla y cada tabla solo puede tener unaPRIMARY KEY
restricción. -
O puede tener múltiples
FOREIGN KEY
restricciones en la(s) misma(s) columna(s) que hacen referencia a unoPRIMARY KEY
de una mesa (diferente) cada uno. (Rara vez útil.)
Sin embargo , un solo PK o FK puede abarcar varias columnas.
Y un FK puede hacer referencia a cualquier (conjunto de) columna(s) única(s) explícitamente definida(s) en el destino, no solo al PK. El manual:
Un PK de varias columnas o UNIQUE
la restricción solo puede ser referenciada por una restricción FK de varias columnas con tipos de columna coincidentes.
Lo que pides
Dado que no está permitido usar la misma columna más de una vez en la lista de columnas de un UNIQUE
o PRIMARY KEY
restricción, la lista de destino de una FOREIGN KEY
Tampoco se puede utilizar la misma columna más de una vez. Pero no hay nada que nos impida usar la misma columna más de una vez en la fuente lista. Aquí radica el potencial para implementar lo que está preguntando (pero probablemente no fue su intención):
"En el team_statistics
tabla el team_statistics.team_id
debe ser una clave externa que haga referencia a matches.team_id
y matches.team_id1
"
La combinación de (team_id, team_id1)
en la tabla matches
necesitaría ser definido UNIQUE
. Valores en team_statistics.team_id
estaría restringido a casos con team = team1
en la tabla matches
como consecuencia 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);
Incluso podría tener sentido para ciertas configuraciones, pero no para la tuya.
Lo que probablemente necesites
Supongo que quieres algo como esto:
(match_id, team_id)
en la tabla team_statistics
debe ser una clave externa que haga referencia a cualquiera (match_id, team_id)
o (match_id, team_id1)
en la tabla matches
.
Y eso no es posible con restricciones FK y solo dos tablas. podrías abusar de un CHECK
restricción con un falso IMMUTABLE
función y convertirlo en NOT VALID
. Consulte el capítulo "Más barato con una restricción CHECK" en esta respuesta:
Pero eso es un engaño avanzado y menos confiable. No es mi sugerencia aquí, así que no voy a dar más detalles. Sugiero normalizar su esquema de una manera ú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)
);
home
marca el equipo local del partido pero, por inclusión en el PK, también restringe a máximo dos equipos por partido . (Las columnas PK están definidas NOT NULL
implícitamente.)
El UNIQUE
opcional restricción en (team_id, match_id)
evita que los equipos jueguen contra ellos mismos. Al usar la secuencia invertida de columnas de índice (irrelevante para hacer cumplir la regla), esto también proporciona un índice complementario al PK, que normalmente también es útil. Ver:
podrías agregue un match_team_statistics
separado , pero eso solo sería una extensión 1:1 opcional para match_team
ahora. Alternativamente, simplemente agregue columnas a match_team
.
Podría agregar vistas para pantallas 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;
Consejo básico: