Een samengestelde externe sleutel is een externe sleutel die uit meerdere kolommen bestaat.
Dit artikel bevat een voorbeeld van het maken van een samengestelde externe sleutel met Transact-SQL in SQL Server.
U kunt een samengestelde externe sleutel maken, net zoals u een enkele externe sleutel zou maken, behalve dat u in plaats van slechts één kolom op te geven, de naam van twee of meer kolommen opgeeft, gescheiden door een komma.
Zoals dit:
CONSTRAINT FK_FKName FOREIGN KEY (FKColumn1, FKColumn2) REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)
Voorbeeld 1 – Maak een samengestelde externe sleutel
Hier is een voorbeeld van een database die een samengestelde externe sleutel (en een samengestelde primaire sleutel) gebruikt.
Voor dit voorbeeld zal ik een database maken met de naam BandTest :
CREATE DATABASE BandTest;
Nu de database is gemaakt, gaan we verder met het maken van de tabellen.
USE BandTest; CREATE TABLE Musician ( MusicianId int NOT NULL, FirstName varchar(60), LastName varchar(60), CONSTRAINT PK_Musician PRIMARY KEY (MusicianID) ); CREATE TABLE Band ( BandId int NOT NULL, BandName varchar(255), CONSTRAINT PK_Band PRIMARY KEY (BandId) ); CREATE TABLE BandMember ( MusicianId int NOT NULL, BandId int NOT NULL, CONSTRAINT PK_BandMember PRIMARY KEY (MusicianID, BandId), CONSTRAINT FK_BandMember_Band FOREIGN KEY (BandId) REFERENCES Band(BandId), CONSTRAINT FK_BandMember_Musician FOREIGN KEY (MusicianId) REFERENCES Musician(MusicianId) ); CREATE TABLE MembershipPeriod ( MembershipPeriodId int NOT NULL, MusicianId int NOT NULL, BandId int NOT NULL, StartDate date NOT NULL, EndDate date NULL, CONSTRAINT PK_MembershipPeriod PRIMARY KEY (MembershipPeriodID), CONSTRAINT FK_MembershipPeriod_BandMember FOREIGN KEY (MusicianID, BandId) REFERENCES BandMember(MusicianID, BandId) );
In dit voorbeeld is de BandMember
tabel heeft een primaire sleutel met meerdere kolommen. De MembershipPeriod
tabel heeft een externe sleutel die verwijst naar die primaire sleutel met meerdere kolommen. Daarom bevatten zowel de primaire als de refererende sleuteldefinities de kolommen gescheiden door een komma.
De redenering achter het bovenstaande databaseontwerp is dat een muzikant mogelijk lid kan zijn van veel bands. Elke band kan ook veel muzikanten hebben. We hebben dus een veel-op-veel relatie. Dit is de reden waarom het BandMember
tabel wordt gemaakt – deze wordt gebruikt als een kruisverwijzingstabel tussen de Musician
tabel en de Band
tafel. In dit geval heb ik ervoor gekozen om een samengestelde primaire sleutel te gebruiken.
Maar een muzikant kan ook meer dan eens lid zijn van een band (een muzikant kan bijvoorbeeld een band verlaten om later terug te keren). Daarom is de MembershipPeriod
tabel kan worden gebruikt om alle periodes vast te leggen waarin elke muzikant lid is geweest van elke band. Dit moet verwijzen naar de samengestelde primaire sleutel op het BandMember
tabel, en dus moet ik een externe sleutel met meerdere kolommen maken.
Voorbeeld 2 – Gegevens invoegen
Nadat ik zojuist de bovenstaande code heb uitgevoerd, kan ik nu de database met gegevens laden:
INSERT INTO Musician VALUES ( 1, 'Ian', 'Paice' ), ( 2, 'Roger', 'Glover' ), ( 3, 'Richie', 'Blackmore' ), ( 4, 'Rod', 'Evans' ), ( 5, 'Ozzy', 'Osbourne' ); INSERT INTO Band VALUES ( 1, 'Deep Purple' ), ( 2, 'Rainbow' ), ( 3, 'Whitesnake' ), ( 4, 'Iron Maiden' ); INSERT INTO BandMember VALUES ( 1, 1 ), ( 1, 3 ), ( 2, 1 ), ( 2, 2 ), ( 3, 1 ), ( 3, 2 ), ( 4, 1 ); INSERT INTO MembershipPeriod VALUES ( 1, 1, 1, '1968-03-01', '1976-03-15' ), ( 2, 1, 1, '1984-04-01', NULL ), ( 3, 1, 3, '1979-08-01', '1982-01-01' ), ( 4, 2, 1, '1969-01-01', '1973-06-29' ), ( 5, 2, 1, '1984-04-01', NULL ), ( 6, 2, 2, '1979-01-01', '1984-01-01' ), ( 7, 3, 1, '1968-03-01', '1975-06-21' ), ( 8, 3, 1, '1984-04-01', '1993-11-17' ), ( 9, 3, 2, '1975-02-01', '1984-04-01' ), ( 10, 3, 2, '1993-11-17', '1997-05-31' ), ( 11, 3, 2, '2015-01-01', NULL ), ( 12, 4, 1, '1968-03-01', '1969-12-01' );
Voorbeeld 3 – Basisquery
Hier is een voorbeeld van een query die op de database kan worden uitgevoerd:
SELECT CONCAT(m.FirstName, ' ', m.LastName) AS 'Musician', b.BandName AS 'Band', mp.StartDate AS 'Start', mp.EndDate AS 'End' FROM Musician m JOIN BandMember bm ON m.MusicianId = bm.MusicianId JOIN Band b ON b.BandId = bm.BandId AND m.MusicianId = bm.MusicianId JOIN MembershipPeriod mp ON mp.BandId = b.BandId AND mp.MusicianId = m.MusicianId;
Resultaat:
+------------------+-------------+------------+------------+ | Musician | Band | Start | End | |------------------+-------------+------------+------------| | Ian Paice | Deep Purple | 1968-03-01 | 1976-03-15 | | Ian Paice | Deep Purple | 1984-04-01 | NULL | | Ian Paice | Whitesnake | 1979-08-01 | 1982-01-01 | | Roger Glover | Deep Purple | 1969-01-01 | 1973-06-29 | | Roger Glover | Deep Purple | 1984-04-01 | NULL | | Roger Glover | Rainbow | 1979-01-01 | 1984-01-01 | | Richie Blackmore | Deep Purple | 1968-03-01 | 1975-06-21 | | Richie Blackmore | Deep Purple | 1984-04-01 | 1993-11-17 | | Richie Blackmore | Rainbow | 1975-02-01 | 1984-04-01 | | Richie Blackmore | Rainbow | 1993-11-17 | 1997-05-31 | | Richie Blackmore | Rainbow | 2015-01-01 | NULL | | Rod Evans | Deep Purple | 1968-03-01 | 1969-12-01 | +------------------+-------------+------------+------------+
We kunnen nu dus zien op welke datums elke muzikant lid was van elke band, zelfs als ze meerdere keren lid waren.
Voorbeeld 4 – Iets gewijzigde zoekopdracht
We zouden de bovenstaande zoekopdracht kunnen wijzigen om de resultaten in een iets beter leesbaar formaat te leveren:
SELECT CONCAT(m.FirstName, ' ', m.LastName) AS 'Musician', b.BandName AS 'Band', STRING_AGG( CONCAT(FORMAT(mp.StartDate, 'yyyy'), '-', ISNULL(FORMAT(mp.EndDate, 'yyyy'), 'present')), ', ') AS 'Time with the band' FROM Musician m JOIN BandMember bm ON m.MusicianId = bm.MusicianId JOIN Band b ON b.BandId = bm.BandId AND m.MusicianId = bm.MusicianId JOIN MembershipPeriod mp ON mp.BandId = b.BandId AND mp.MusicianId = m.MusicianId GROUP BY m.FirstName, m.LastName, b.BandName;
Resultaat:
+------------------+-------------+------------------------------------+ | Musician | Band | Time with the band | |------------------+-------------+------------------------------------| | Ian Paice | Deep Purple | 1968-1976, 1984-present | | Ian Paice | Whitesnake | 1979-1982 | | Richie Blackmore | Deep Purple | 1968-1975, 1984-1993 | | Richie Blackmore | Rainbow | 1975-1984, 1993-1997, 2015-present | | Rod Evans | Deep Purple | 1968-1969 | | Roger Glover | Deep Purple | 1969-1973, 1984-present | | Roger Glover | Rainbow | 1979-1984 | +------------------+-------------+------------------------------------+
Dit voorbeeld maakt gebruik van de STRING_AGG()
functie om de verschillende tijdsperioden voor elke muzikant samen te voegen. Dit vermindert uiteindelijk het aantal vereiste rijen en stelt ons in staat om de tijdsperioden samen in hetzelfde veld te groeperen.
Ik maak ook gebruik van de ISNULL()
functie, waarmee ik alle NULL-waarden in iets zinvoller kan veranderen.
Merk op dat ISNULL()
vereist dat het tweede argument van een type is dat impliciet kan worden geconverteerd naar het type van het eerste argument. In dit geval was het eerste argument oorspronkelijk een datum type, wat betekent dat ik geen string zou kunnen gebruiken. In dit geval heb ik echter besloten om de FORMAT()
. te gebruiken functie om de datum op te maken waarde. Deze functie converteert impliciet de datum waarde toe aan een string, en daarom kon ik een string gebruiken voor het tweede argument.