PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Comment faire pointer une clé étrangère sur deux clés primaires ?

Règles pour les contraintes FK

Pour répondre à la question dans le titre et à la fin de votre texte :

"J'aimerais quand même savoir comment avoir une clé étrangère référençant deux clés primaires."

C'est impossible.

  • Une FOREIGN KEY la contrainte ne peut pointer que vers une table et chaque table ne peut en avoir qu'une PRIMARY KEY contrainte.

  • Ou vous pouvez en avoir plusieurs FOREIGN KEY contraintes sur la ou les mêmes colonnes faisant référence à une PRIMARY KEY d'une table (différente) chacun. (Rarement utile.)

Cependant , un seul PK ou FK peut s'étendre sur plusieurs colonnes.
Et un FK peut faire référence à n'importe quel (ensemble de) colonne(s) unique(s) explicitement défini(s) dans la cible, pas seulement le PK. Le manuel :

Une PK multicolonne ou UNIQUE La contrainte ne peut être référencée que par une contrainte FK multicolonne avec des types de colonne correspondants.

Ce que vous demandez

Puisqu'il n'est pas permis d'utiliser la même colonne plus d'une fois dans la liste des colonnes d'un UNIQUE ou PRIMARY KEY contrainte, la liste cible d'une FOREIGN KEY ne peut pas non plus utiliser la même colonne plus d'une fois. Mais rien ne nous empêche d'utiliser la même colonne plus d'une fois dans la source liste. C'est là que réside la possibilité de mettre en œuvre ce que vous demandez (mais que vous n'avez probablement pas voulu) :

"Dans les team_statistics tablez le team_statistics.team_id doit être une clé étrangère faisant référence à matches.team_id et matches.team_id1 "

La combinaison de (team_id, team_id1) dans le tableau matches devra être défini UNIQUE . Valeurs dans team_statistics.team_id serait limité aux cas avec team = team1 dans le tableau matches comme conséquence logique :

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);

Peut-être même logique pour certaines configurations, mais pas la vôtre.

Ce dont vous avez probablement besoin

Ma supposition éclairée est que vous voulez quelque chose comme ça :

(match_id, team_id) dans le tableau team_statistics doit être une clé étrangère qui fait référence à soit (match_id, team_id) ou (match_id, team_id1) dans le tableau matches .

Et ce n'est pas possible avec les contraintes FK et seulement deux tables. Vous pourriez abuser d'un CHECK contrainte avec un faux IMMUTABLE fonction et rendez-la NOT VALID . Voir le chapitre "Moins cher avec une contrainte CHECK" dans cette réponse :

Mais c'est une supercherie avancée et moins fiable. Ce n'est pas ma suggestion ici, donc je ne vais pas élaborer. Je suggère de normaliser votre schéma d'une manière utile, comme :

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 marque l'équipe à domicile du match mais, par inclusion dans le PK, limite également à deux équipes maximum par match . (Les colonnes PK sont définies NOT NULL implicitement.)

L'option UNIQUE contrainte sur (team_id, match_id) empêche les équipes de jouer contre elles-mêmes. En utilisant la séquence inversée des colonnes d'index (non pertinente pour l'application de la règle), cela fournit également un index complémentaire au PK, ce qui est généralement également utile. Voir :

Vous pourriez ajouter un match_team_statistics séparé , mais ce ne serait qu'une extension facultative 1:1 de match_team à présent. Sinon, ajoutez simplement des colonnes à match_team .

Je pourrais ajouter des vues pour les affichages typiques, comme :

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;

Conseil de base :