Regeln für FK-Einschränkungen
Um die Frage im Titel und am Ende Ihres Textes zu beantworten:
"Ich würde immer noch gerne wissen, wie man einen Fremdschlüssel hat, der auf zwei Primärschlüssel verweist."
Das ist unmöglich.
-
Ein
FOREIGN KEY
Einschränkung kann nur auf eine zeigen Tabelle und jede Tabelle kann nur eine habenPRIMARY KEY
Einschränkung. -
Oder Sie können mehrere haben
FOREIGN KEY
Beschränkungen für dieselbe(n) Spalte(n), die auf eine verweisenPRIMARY KEY
jeweils einer (anderen) Tabelle. (Selten nützlich.)
Allerdings , ein einzelnes PK oder FK kann sich über mehrere Spalten erstrecken.
Und ein FK kann auf jede explizit definierte eindeutige(n) Spalte(n) im Ziel verweisen, nicht nur auf den PK. Das Handbuch:
Ein mehrspaltiger PK oder UNIQUE
Einschränkung kann nur von einer mehrspaltigen FK-Einschränkung mit übereinstimmenden Spaltentypen referenziert werden.
Was Sie fragen
Da es nicht erlaubt ist, dieselbe Spalte mehr als einmal in der Spaltenliste eines UNIQUE
zu verwenden oder PRIMARY KEY
Einschränkung, die Zielliste eines FOREIGN KEY
kann dieselbe Spalte auch nicht mehr als einmal verwenden. Aber nichts hindert uns daran, dieselbe Spalte mehr als einmal in der Quelle zu verwenden aufführen. Hierin liegt das Potenzial, das umzusetzen, was Sie verlangen (aber wahrscheinlich nicht beabsichtigten):
"In der team_statistics
Tabelle team_statistics.team_id
sollte ein Fremdschlüssel sein, der auf matches.team_id
verweist und matches.team_id1
"
Die Kombination aus (team_id, team_id1)
in der Tabelle matches
müsste UNIQUE
definiert werden . Werte in team_statistics.team_id
wäre auf Fälle mit team = team1
beschränkt in der Tabelle matches
als logische Konsequenz:
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);
Könnte für bestimmte Setups sogar sinnvoll sein, aber nicht für deins.
Was Sie wahrscheinlich brauchen
Meine begründete Vermutung ist, dass Sie so etwas wollen:
(match_id, team_id)
in der Tabelle team_statistics
sollte ein Fremdschlüssel sein, der auf entweder verweist (match_id, team_id)
oder (match_id, team_id1)
in der Tabelle matches
.
Und das ist mit FK-Einschränkungen und nur zwei Tabellen nicht möglich. Sie könnten einen CHECK
missbrauchen Einschränkung mit einem gefälschten IMMUTABLE
Funktion und machen Sie es zu NOT VALID
. Siehe Kapitel "Billiger mit einer CHECK-Einschränkung" in dieser Antwort:
Aber das ist fortgeschrittene Trickserei und weniger zuverlässig. Nicht mein Vorschlag hier, also werde ich nicht näher darauf eingehen. Ich schlage vor, normalisieren Ihr Schema auf nützliche Weise, wie:
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
markiert die Heimmannschaft des Spiels, ist aber durch Aufnahme in die PK auch auf maximal zwei Mannschaften pro Spiel beschränkt . (PK-Spalten sind NOT NULL
definiert implizit.)
Der optionale UNIQUE
Beschränkung auf (team_id, match_id)
hindert Teams daran, gegen sich selbst zu spielen. Durch die Verwendung der umgekehrten Reihenfolge der Indexspalten (für die Durchsetzung der Regel nicht relevant) stellt dies auch einen Index bereit, der komplementär zum PK ist, was typischerweise auch nützlich ist. Siehe:
Sie könnten füge eine separate match_team_statistics
hinzu , aber das wäre nur eine optionale 1:1-Erweiterung zu match_team
jetzt. Alternativ fügen Sie einfach Spalten zu match_team
hinzu .
Ich könnte Ansichten hinzufügen für typische Displays, wie:
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;
Grundlegende Ratschläge: