En sammensat fremmednøgle er en fremmednøgle, der består af flere kolonner.
Denne artikel giver et eksempel på oprettelse af en sammensat fremmednøgle ved hjælp af Transact-SQL i SQL Server.
Du kan oprette en sammensat fremmednøgle, ligesom du ville oprette en enkelt fremmednøgle, bortset fra at du i stedet for kun at angive én kolonne angiver navnet på to eller flere kolonner, adskilt af et komma.
Sådan:
CONSTRAINT FK_FKName FOREIGN KEY (FKColumn1, FKColumn2) REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)
Eksempel 1 – Opret en sammensat fremmednøgle
Her er et eksempel på en database, der bruger en sammensat fremmednøgle (og en sammensat primærnøgle).
Til formålet med dette eksempel vil jeg oprette en database kaldet BandTest :
CREATE DATABASE BandTest;
Nu hvor databasen er oprettet, lad os gå videre og oprette tabellerne.
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) );
I dette eksempel er BandMember
tabellen har en primær nøgle med flere kolonner. MembershipPeriod
tabellen har en fremmednøgle, der refererer til den primære nøgle med flere kolonner. Derfor inkluderer både primær- og fremmednøgledefinitionerne kolonnerne adskilt af et komma.
Begrundelsen bag ovenstående databasedesign er, at en musiker potentielt kan være medlem af mange bands. Desuden kan hvert band have mange musikere. Så vi har et mange-til-mange forhold. Det er derfor BandMember
tabel oprettes – den bruges som en krydsreferencetabel mellem Musician
tabellen og Band
bord. I dette tilfælde har jeg valgt at bruge en sammensat primærnøgle.
Men en musiker kan også være medlem af et band ved mere end én lejlighed (f.eks. kan en musiker forlade et band for at vende tilbage senere). Derfor er MembershipPeriod
tabel kan bruges til at optage alle de perioder, hver musiker har været medlem af hvert band. Dette skal referere til den sammensatte primærnøgle på BandMember
tabel, og derfor skal jeg oprette en fremmednøgle med flere kolonner.
Eksempel 2 – Indsæt data
Efter lige at have kørt ovenstående kode, kan jeg nu indlæse databasen med data:
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' );
Eksempel 3 – Grundlæggende forespørgsel
Her er et eksempel på en forespørgsel, der kunne køres mod databasen:
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;
Resultat:
+------------------+-------------+------------+------------+ | 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 | +------------------+-------------+------------+------------+
Så vi kan nu se, hvilke datoer hver musiker var medlem af hvert band, selvom de var medlem ved flere lejligheder.
Eksempel 4 – Lidt ændret forespørgsel
Vi kunne ændre ovenstående forespørgsel for at give resultaterne i et lidt mere læsbart format:
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;
Resultat:
+------------------+-------------+------------------------------------+ | 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 | +------------------+-------------+------------------------------------+
Dette eksempel udnytter STRING_AGG()
funktion til at sammenkæde de forskellige tidsperioder for hver musiker. Dette ender med at reducere antallet af krævede rækker og giver os mulighed for at gruppere tidsperioderne i det samme felt.
Jeg udnytter også ISNULL()
funktion, som giver mig mulighed for at ændre alle NULL-værdier til noget mere meningsfuldt.
Bemærk at ISNULL()
kræver, at det andet argument er af en type, der implicit kan konverteres til typen af det første argument. I dette tilfælde var det første argument oprindeligt en dato type, hvilket betyder, at jeg ikke ville være i stand til at bruge en streng. Men i dette tilfælde besluttede jeg at bruge FORMAT()
funktion til at formatere datoen værdi. Denne funktion konverterer implicit datoen værdi til en streng, og derfor var jeg i stand til at bruge en streng til det andet argument.