Vreemde sleutels zijn een integraal onderdeel van het creëren van een relatie in relationele databases. Hier leest u waarom en hoe u ze kunt maken.
We hebben dus vastgesteld dat een primaire sleutel een unieke id voor de tabel biedt. Maar primaire sleutels zijn niet het enige "sleutel"-type. Onze database kan ook externe sleutels bevatten.
Wat is een buitenlandse sleutel?
Een vreemde sleutel is een kolom (of verzameling kolommen) in een tabel die op unieke wijze een rij van een andere tabel identificeert. Dit definieert een relatie tussen de twee tabellen.
Met een externe sleutel kunt u gerelateerde gegevens in tabellen kruisen. Dit is handig wanneer een kolom gegevens bevat die in een andere tabel worden weergegeven.
Voorbeeld
Hier is een diagram van onze FruitShop database met de relatie tussen de Fruit tabel en de Eenheden tafel.
De zwarte lijn die de twee tabellen met elkaar verbindt, geeft een externe sleutel aan. De UnitId veld op de Fruit tabel is een externe sleutel voor de UnitId veld op de Eenheden tafel. Daarom is de waarde die we invoegen in Fruit.UnitId moet overeenkomen met een waarde in Units.UnitId . Dit schakelt de Fruit.UnitId om te verwijzen naar de gegevens in de andere kolommen voor die record (d.w.z. de record met de corresponderende UnitId ).
De gegevens
Dus als onze Fruit tabel bevat een record als dit:
FruitId | FruitName | Inventaris | UnitId | Datum ingevoerd | Datum bijgewerkt |
---|---|---|---|---|---|
1 | Appel | 10 | 3 | 2012-11-27 12:42:10 | 2012-11-27 12:42:10 |
En onze Eenheden tabel bevat de volgende records:
UnitId | Eenheidsnaam | Datum ingevoerd | Datum bijgewerkt |
---|---|---|---|
1 | Stuk | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
2 | Stel | 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 | Container | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
5 | Pond | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
6 | Ounce | 2011-12-30 12:46:15 | 2011-12-30 12:46:15 |
U kunt zien dat de
Fruit.UnitId
veld bevat een 3
. Kijk nu naar de
Eenheden
tabel voor de record die een 3
. bevat in de
UnitId
veld. U kunt zien dat dit record
Kilogram
vertegenwoordigt . Daarom weten we nu dat appels worden gemeten in kilogram.
Het goede aan het op deze manier instellen van de database is dat we niet "Kilogrammen" hoeven te herhalen voor elk record dat die eenheid gebruikt. Het verminderen van duplicatie is een belangrijk voordeel van relationele databasebeheersystemen.
Zoveel records zien in de Fruit tabel dezelfde eenheidsnaam zal delen (bijv. "Kilogram", "Container", "Bunch", enz.), moeten we goed nadenken voordat we duplicaten aan onze database toevoegen. Zonder een refererende-sleutelrelatie te gebruiken, kunnen we de eenheidsnamen gewoon rechtstreeks in de Fruit . schrijven tabel (en misschien de kolom "Unit", "UnitType" of "UnitName" noemen). Dan zouden we eindigen met veel records die dezelfde waarde delen voor de kolom met de naam van de eenheid. We zouden "Kilogram" keer op keer zien herhalen tegen vele records. We zouden ook "Bunch" herhaald zien, en elk ander populair eenheidstype.
Hoewel het niet per se "fout" is om dit te doen, is het over het algemeen efficiënter om één record voor elke eenheidsnaam in een aparte tabel op te slaan en vervolgens naar die tabel te verwijzen via de UnitId kolom. Dit is efficiënter dan die eenheidsnamen keer op keer te herhalen voor elke record die is gemaakt in de Fruits tafel. Het maakt het ook gemakkelijker als we ooit besluiten een eenheidsnaam bij te werken (wijzig bijvoorbeeld "Kilogram" in "Kilo's). Als we de naam van een eenheid bijwerken, heeft dit geen invloed op de Fruit tabel omdat de UnitId zal hetzelfde blijven. Bovendien helpt het ook te voorkomen dat er inconsistente gegevens in onze database verschijnen.
Buitenlandse sleutelbeperking
Een externe-sleutelbeperking is een databaseobject dat helpt bij het consistent houden van uw externe-sleutelgegevens. U maakt een externe sleutelbeperking om de referentiële integriteit te behouden. Door een externe sleutelbeperking te creëren, vertelt u MySQL om bepaalde regels over de gegevens af te dwingen. Wanneer gegevens worden ingevoegd, verwijderd of bijgewerkt, controleert MySQL of deze overeenkomen met de externe sleutel die u tussen tabellen hebt gemaakt. Als dit niet het geval is, wordt voorkomen dat de gegevens worden geschreven/overschreven/verwijderd, waardoor de referentiële integriteit behouden blijft.
Als een gebruiker bijvoorbeeld een UnitId-waarde probeert in te voeren in de Fruit.UnitId kolom maar er is geen corresponderend record in de Units.UnitId kolom, dan zal MySQL voorkomen dat de gebruiker die waarde invoert.
Toen we onze twee tabellen maakten, hebben we een externe sleutelbeperking toegevoegd aan de Fruit tafel. Dit is de code die we hebben gebruikt om de beperking te maken:
CONSTRAINT fkFruitUnits FOREIGN KEY (UnitId) REFERENCES Units (UnitId) ON DELETE RESTRICT ON UPDATE CASCADE
Wanneer u de knooppunten in de linker SCHEMAS . uitvouwt tabblad kunt u de externe sleutel zien die we hebben gemaakt (evenals de primaire sleutels):
Als u gegevens probeert in te voegen die niet voldoen aan de externe-sleutelbeperking, zou u een foutmelding moeten krijgen.
Als ik bijvoorbeeld probeer een record in te voegen in de Fruit tabel met een UnitId waarde die niet bestaat in de Eenheden tabel, krijg ik de volgende foutmelding:
Dit gebeurt omdat ik een waarde van 5
probeer in te voeren in de
UnitId
kolom als er geen corresponderende waarde is in de
Units.UnitId
veld.
Om dit te laten slagen, moet ik ervoor zorgen dat er een record is in de
Units
tabel met een
UnitId
van 5
.
Buitenlandse sleutel werkt niet?
U kunt af en toe een situatie tegenkomen waarin een externe sleutel niet lijkt te werken. U kunt bijvoorbeeld met succes gegevens in een tabel invoegen, ook al is er een externe sleutel die moet voorkomen dat die gegevens worden ingevoegd.
Er zijn een paar dingen die u in deze situatie kunt controleren.
- Zorg ervoor dat u
ON DELETE
heeft toegevoegd enON UPDATE
clausules in uw code. Bijvoorbeeld:ON DELETE RESTRICT ON UPDATE CASCADE
. Zie ons CREATE TABLE-voorbeeld voor wanneer u deze code moet plaatsen. - Zorg ervoor dat de tabel InnoDB is . U kunt dit doen door
ENGINE=InnoDB
. toe te voegen aan het einde van uwCREATE TABLE
statement (zie mijn voorbeeld van toen we onze tabellen maakten). Sommige engines (zoals MyISAM ) bieden geen ondersteuning voor externe-sleutelbeperkingen, maar ze geven hier geen waarschuwing over wanneer u probeert uw externe-sleutelbeperking te creëren. Als uw standaardengine niet InnoDB is dan worden uw externe sleutels waarschijnlijk niet ondersteund. - Zorg ervoor dat MySQL de externe sleutels daadwerkelijk controleert. U kunt dit doen door de volgende code uit te voeren:
SET FOREIGN_KEY_CHECKS=1
.
Controleer buitenlandse sleutels uitschakelen
Er kunnen momenten zijn waarop beperkingen voor externe sleutels onnodig beperkend kunnen worden - tot het punt waarop ze uw inspanningen bij het laden van gegevens ernstig belemmeren. Bijvoorbeeld wanneer u net een database hebt gemaakt en u moet de initiële gegevens laden. Of als u een aantal tabellen moet laten vallen en de gegevens opnieuw moet laden.
Als u de gegevens niet in de juiste volgorde laadt, krijgt u waarschijnlijk fouten met externe sleutels doordat de gegevens in de verkeerde volgorde worden geladen (d.w.z. u probeert de onderliggende tabellen te laden voordat de bovenliggende tabellen hun gegevens geladen).
Dit is niet alleen een probleem bij het laden de data. U kunt dit probleem ook tegenkomen bij het maken van de database. Als u de tabellen niet in de juiste volgorde maakt, kunt u fouten tegenkomen vanwege beperkingen met externe sleutels.
Als u de juiste ouder-kind-volgorde niet weet, kan het mogelijk veel tijd en moeite kosten om de juiste volgorde te bepalen voor het maken van de database of het laden van de gegevens. In dit soort gevallen is het misschien beter om MySQL te vertellen dat het voorlopig geen externe sleutels moet controleren.
U kunt het controleren van externe sleutels uitschakelen met de volgende code:
FOREIGN_KEY_CHECKS=0
Ga als volgt te werk om het weer in te schakelen:
FOREIGN_KEY_CHECKS=1