Regler for FK-begrænsninger
For at besvare spørgsmålet i titlen og i slutningen af din tekst:
"Jeg vil stadig gerne vide, hvordan man har én fremmednøgle, der refererer til to primære nøgler."
Det er umuligt.
-
En
UDLANDS NØGLE
begrænsning kan kun pege på én tabel, og hver tabel kan kun have énPRIMÆR NØGLE
begrænsning. -
Eller du kan have flere
UDLANDS NØGLE
begrænsninger på samme kolonne(r), der refererer til énPRIMÆR NØGLE
af en (forskellig) tabel hver. (Sjældent nyttigt.)
Men , en enkelt PK eller FK kan spænder over flere kolonner.
Og en FK kan referere til enhver eksplicit defineret unik (sæt af) kolonne(r) i målet, ikke kun PK. Manualen:
En multikolonne PK eller UNIK
begrænsning kan kun refereres af en FK-begrænsning med flere kolonner med matchende kolonnetyper.
Hvad du spørger om
Da det ikke er tilladt at bruge den samme kolonne mere end én gang i kolonnelisten for en UNIQUE
eller PRIMÆR NØGLE
constraint, mållisten for en FOREIGN KEY
kan heller ikke bruge samme kolonne mere end én gang. Men der er ikke noget, der forhindrer os i at bruge den samme kolonne mere end én gang i kilden liste. Heri ligger potentialet til at implementere det, du spørger om (men sandsynligvis ikke har tænkt dig):
"I team_statistics
tabel team_statistics.team_id
skal være en fremmednøgle, der refererer til matches.team_id
og matches.team_id1
"
Kombinationen af (team_id, team_id1)
i tabel matches
skal defineres UNIQUE
. Værdier i team_statistics.team_id
ville være begrænset til sager med team =team1
i tabel matches
som logisk konsekvens:
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);
Måske giver det endda mening for visse opsætninger, men ikke dine.
Hvad du sandsynligvis har brug for
Mit veluddannede gæt er, at du vil have sådan noget her:
(match_id, team_id)
i tabel team_statistics
skal være en fremmednøgle, der henviser til enten (match_id, team_id)
eller (match_id, team_id1)
i tabel matches
.
Og det er ikke muligt med FK-begrænsninger og kun to borde. Du kunne misbruge en CHECK
begrænsning med en falsk IMMUTABLE
funktion og gør den IKKE GYLDIG
. Se kapitlet "Billigere med en CHECK-begrænsning" i dette svar:
Men det er avanceret trick og mindre pålideligt. Ikke mit forslag her, så jeg vil ikke uddybe det. Jeg foreslår at normalisere dit skema på en nyttig måde, såsom:
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)
);
hjem
markerer kampens hjemmehold, men ved medtagelse i PK begrænser det også til maks. to hold pr. kamp . (PK-kolonner er defineret IKKE NULL
implicit.)
Den valgfrie UNIQUE
begrænsning på (team_id, match_id)
forhindrer holdene i at spille mod sig selv. Ved at bruge den omvendte sekvens af indekskolonner (irrelevant for at håndhæve reglen) giver dette også et indeks, der er komplementært til PK, hvilket typisk også er nyttigt. Se:
Du kunne tilføje en separat match_team_statistics
, men det ville bare være en valgfri 1:1-udvidelse til match_team
nu. Alternativt kan du blot tilføje kolonner til match_team
.
Jeg kan tilføje visninger til typiske skærme, såsom:
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;
Grundlæggende råd: