Fremmednøgler er en integreret del af at skabe en relation i relationelle databaser. Her er hvorfor, og hvordan du opretter dem.
Så vi har fastslået, at en primær nøgle giver en unik identifikator for tabellen. Men primærnøgler er ikke den eneste "nøgle"-type. Vores database kan også indeholde fremmednøgler.
Hvad er en fremmednøgle?
En fremmednøgle er en kolonne (eller samling af kolonner) i en tabel, der unikt identificerer en række i en anden tabel. Dette definerer en relation mellem de to tabeller.
En fremmednøgle giver dig mulighed for at krydshenvise relaterede data på tværs af tabeller. Dette er praktisk, når en kolonne indeholder data, der er repræsenteret i en anden tabel.
Eksempel
Her er et diagram over vores FruitShop database, der viser forholdet mellem Fruit tabellen og Enhederne bord.
Den sorte linje, der forbinder de to tabeller, angiver en fremmednøgle. UnitId feltet på Fruit tabel er en fremmednøgle til UnitId feltet på Enheder bord. Derfor er værdien, som vi indsætter i Fruit.UnitId skal svare til en værdi i Units.UnitId . Dette aktiverer Fruit.UnitId for at referere til dataene i de andre kolonner for den pågældende post (dvs. den post, der har det tilsvarende UnitId ).
Dataene
Så hvis vores Fruit tabel indeholder en post som denne:
FruitId | FruitName | Beholdning | UnitId | DateEntered | Opdateret dato |
---|---|---|---|---|---|
1 | Apple | 10 | 3 | 2012-11-27 12:42:10 | 2012-11-27 12:42:10 |
Og vores Enheder tabel indeholder følgende poster:
UnitId | Enhedsnavn | DateEntered | Opdateret dato |
---|---|---|---|
1 | Styk | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
2 | Flok | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
3 | Kilogram | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
4 | Beholder | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
5 | Pund | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
6 | Once | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
Du kan se, at
Fruit.UnitId
feltet indeholder en 3
. Se nu på
Enheder
tabel for posten, der indeholder en 3
i
UnitId
Mark. Du kan se, at denne post repræsenterer
Kilogram
. Derfor ved vi nu, at æbler måles i kilo.
Det gode ved at opsætte databasen på denne måde er, at vi ikke behøver at gentage "Kilogram" for hver post, der bruger den enhed. Reduktion af duplikering er en vigtig fordel ved relationelle databasestyringssystemer.
Ser lige så mange poster i Fruit tabel vil dele det samme enhedsnavn (f.eks. "Kilograms", "Container", "Bunch" osv.), bør vi tænke os godt om, før vi tilføjer dubletter til vores database. Uden at bruge et fremmednøgleforhold kunne vi bare skrive enhedsnavnene direkte ind i Fruit tabel (og måske kalde kolonnen "Unit", "UnitType" eller "UnitName"). Så ville vi ende med at mange poster deler den samme værdi for kolonnen enhedsnavn. Vi ville se "Kilogram" gentaget igen og igen mod mange plader. Vi ville også se "Bunch" gentaget, og enhver anden populær enhedstype.
Selvom det ikke nødvendigvis er "forkert" at gøre dette, er det generelt mere effektivt at gemme én post for hver af disse enhedsnavne i en separat tabel og derefter henvise til den tabel via UnitId kolonne. At gøre dette er mere effektivt end at gentage disse enhedsnavne igen og igen for hver post, der oprettes i Fruits bord. Det gør det også nemmere, hvis vi nogensinde beslutter os for at opdatere et enhedsnavn (skift f.eks. "Kilograms" til "Kilos"). Hvis vi opdaterer et enhedsnavn, vil det ikke påvirke Fruit tabel, fordi UnitId vil forblive den samme. Derudover hjælper det også med at forhindre, at inkonsistente data dukker op i vores database.
Foreign Key Constraint
En fremmednøglebegrænsning er et databaseobjekt, der hjælper med at holde dine fremmednøgledata konsistente. Du opretter en fremmednøglebegrænsning for at opretholde referenceintegritet. Ved at oprette en fremmednøgle-begrænsning fortæller du MySQL om at håndhæve visse regler over dataene. Når data indsættes, slettes eller opdateres, vil MySQL kontrollere, at den overholder den fremmednøgle, som du har oprettet mellem tabeller. Hvis ikke, vil det forhindre, at dataene skrives/overskrives/slettes, og dermed bevares referentiel integritet.
For eksempel, hvis en bruger forsøger at indtaste en UnitId-værdi i Fruit.UnitId kolonnen, men der er ingen tilsvarende post i Units.UnitId kolonne, så vil MySQL forhindre brugeren i at indtaste denne værdi.
Da vi oprettede vores to tabeller, føjede vi en fremmednøglebegrænsning til Fruit bord. Her er koden, vi brugte til at oprette begrænsningen:
CONSTRAINT fkFruitUnits FOREIGN KEY (UnitId) REFERENCES Units (UnitId) ON DELETE RESTRICT ON UPDATE CASCADE
Når du udvider noderne i venstre SCHEMAS fanen, kan du se den fremmednøgle, vi oprettede (såvel som de primære nøgler):
Hvis du forsøger at indsætte data, der ikke er i overensstemmelse med begrænsningen for fremmednøgle, skulle du få en fejl.
For eksempel, hvis jeg forsøger at indsætte en post i Fruit tabel ved hjælp af et UnitId værdi, der ikke findes i Enheder tabel, modtager jeg følgende fejl:
Dette sker, fordi jeg forsøger at indsætte en værdi på 5
ind i
UnitId
kolonne, når der ikke er nogen tilsvarende værdi i
Units.UnitId
felt.
For at dette skal lykkes, skal jeg sikre, at der er en rekord i
Enheder
tabel med et
UnitId
af 5
.
Udenlandsk nøgle virker ikke?
Du kan muligvis støde på en situation, hvor en fremmednøgle ikke ser ud til at virke. Du kan f.eks. indsætte data i en tabel, selvom der er en fremmednøgle, der skulle forhindre disse data i at blive indsat.
Der er et par ting, du kan tjekke i denne situation.
- Sørg for, at du har tilføjet
ON DELETE
ogON UPDATE
klausuler i din kode. For eksempelON DELETE RESTRICT ON UPDATE CASCADE
. Se vores CREATE TABLE-eksempel for, hvornår denne kode skal placeres. - Sørg for, at tabellen er InnoDB . Du kan gøre dette ved at tilføje
ENGINE=InnoDB
til slutningen af dinCREATE TABLE
statement (se mit eksempel fra da vi lavede vores tabeller). Nogle motorer (såsom MyISAM ) understøtter ikke begrænsninger for fremmednøgle, men de giver ingen advarsel om dette, når du forsøger at oprette din begrænsning af fremmednøgle. Hvis din standardmotor ikke er InnoDB så er det sandsynligt, at dine fremmednøgler ikke bliver understøttet. - Sørg for, at MySQL rent faktisk tjekker fremmednøgler. Du kan gøre dette ved at køre følgende kode:
SET FOREIGN_KEY_CHECKS=1
.
Deaktiver kontrol af fremmednøgler
Der kan være tidspunkter, hvor fremmednøglebegrænsninger kan blive unødvendigt restriktive - til det punkt, hvor de i alvorlig grad hæmmer din indsats for at indlæse data. For eksempel, når du lige har oprettet en database, og du skal indlæse de oprindelige data. Eller hvis du har brug for at slippe en masse tabeller og genindlæse dataene.
Hvis du ikke indlæser dataene i den rigtige rækkefølge, vil du sandsynligvis blive ved med at få fejl med en fremmednøgle på grund af, at dataene bliver indlæst i den forkerte rækkefølge (dvs. du forsøger at indlæse de underordnede tabeller, før de overordnede tabeller har fået deres data indlæst).
Dette er ikke kun et problem ved indlæsning dataene. Du kan også støde på dette problem, når du opretter databasen i første omgang. Hvis du ikke opretter tabellerne i den rigtige rækkefølge, kan du støde på fejl på grund af eventuelle fremmednøglebegrænsninger.
Hvis du ikke kender den korrekte forældre-underordnede rækkefølge, kan det muligvis tage en masse tid og kræfter at etablere den korrekte rækkefølge for at oprette databasen eller indlæse dataene. I tilfælde som disse er det måske bedre at fortælle MySQL midlertidigt ikke at tjekke fremmednøgler for nu.
Du kan deaktivere kontrol af fremmednøgle med følgende kode:
FOREIGN_KEY_CHECKS=0
Gør dette for at aktivere det igen:
FOREIGN_KEY_CHECKS=1