PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Wie kann man einen Fremdschlüssel haben, der auf zwei Primärschlüssel zeigt?

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 haben PRIMARY KEY Einschränkung.

  • Oder Sie können mehrere haben FOREIGN KEY Beschränkungen für dieselbe(n) Spalte(n), die auf eine verweisen PRIMARY 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: