En sammansatt främmande nyckel är en främmande nyckel som består av flera kolumner.
Den här artikeln ger ett exempel på hur du skapar en sammansatt främmande nyckel med Transact-SQL i SQL Server.
Du kan skapa en sammansatt främmande nyckel precis som du skulle skapa en enskild främmande nyckel, förutom att istället för att ange bara en kolumn, anger du namnet på två eller flera kolumner, åtskilda med kommatecken.
Så här:
CONSTRAINT FK_FKName FOREIGN KEY (FKColumn1, FKColumn2) REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)
Exempel 1 – Skapa en sammansatt främmande nyckel
Här är ett exempel på en databas som använder en sammansatt främmande nyckel (och en sammansatt primärnyckel).
För detta exempel skapar jag en databas som heter BandTest :
CREATE DATABASE BandTest;
Nu när databasen har skapats, låt oss gå vidare och skapa tabellerna.
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 det här exemplet är BandMember
tabellen har en primärnyckel med flera kolumner. MembershipPeriod
tabellen har en främmande nyckel som refererar till den primärnyckeln med flera kolumner. Därför inkluderar både primär- och främmande nyckeldefinitionerna kolumner separerade med kommatecken.
Resonemanget bakom ovanstående databasdesign är att en musiker potentiellt kan vara medlem i många band. Dessutom kan varje band ha många musiker. Så vi har ett många-till-många-förhållande. Det är därför BandMember
tabellen skapas – den används som en korsreferenstabell mellan Musician
tabellen och Band
tabell. I det här fallet har jag valt att använda en sammansatt primärnyckel.
Men en musiker kan också vara medlem i ett band vid mer än ett tillfälle (t.ex. kan en musiker lämna ett band för att återkomma senare). Därför är MembershipPeriod
tabellen kan användas för att spela in alla perioder varje musiker har varit medlem i varje band. Detta måste referera till den sammansatta primärnyckeln på BandMember
tabell, så jag måste skapa en främmande nyckel med flera kolumner.
Exempel 2 – Infoga data
Efter att ha kört ovanstående kod kan jag nu ladda 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' );
Exempel 3 – Grundläggande fråga
Här är ett exempel på en fråga som kan köras mot 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 vilka datum varje musiker var medlem i varje band, även om de var medlem vid flera tillfällen.
Exempel 4 – Något modifierad fråga
Vi skulle kunna ändra ovanstående fråga för att ge resultaten i ett lite mer 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 | +------------------+-------------+------------------------------------+
Det här exemplet drar fördel av STRING_AGG()
funktion för att sammanfoga de olika tidsperioderna för varje musiker. Detta slutar med att minska antalet rader som krävs och gör att vi kan gruppera tidsperioderna i samma fält.
Jag drar också fördel av ISNULL()
funktion, som låter mig ändra alla NULL-värden till något mer meningsfullt.
Observera att ISNULL()
kräver att det andra argumentet är av en typ som implicit kan konverteras till typen av det första argumentet. I det här fallet var det första argumentet ursprungligen ett datum typ, vilket betyder att jag inte skulle kunna använda en sträng. Men i det här fallet bestämde jag mig för att använda FORMAT()
funktion för att formatera datumet värde. Denna funktion konverterar implicit datumet värde till en sträng, och därför kunde jag använda en sträng för det andra argumentet.