Một khóa ngoại tổng hợp là một khóa ngoại bao gồm nhiều cột.
Bài viết này cung cấp một ví dụ về cách tạo khóa ngoại tổng hợp bằng Transact-SQL trong SQL Server.
Bạn có thể tạo khóa ngoại tổng hợp giống như cách tạo một khóa ngoại duy nhất, ngoại trừ việc thay vì chỉ định một cột, bạn cung cấp tên của hai hoặc nhiều cột, được phân tách bằng dấu phẩy.
Như thế này:
CONSTRAINT FK_FKName FOREIGN KEY (FKColumn1, FKColumn2) REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)
Ví dụ 1 - Tạo một khóa ngoại hỗn hợp
Dưới đây là ví dụ về cơ sở dữ liệu sử dụng khóa ngoại tổng hợp (và khóa chính tổng hợp).
Đối với mục đích của ví dụ này, tôi sẽ tạo một cơ sở dữ liệu có tên là BandTest :
CREATE DATABASE BandTest;
Bây giờ cơ sở dữ liệu đã được tạo, hãy tiếp tục và tạo các bảng.
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) );
Trong ví dụ này, BandMember
bảng có khóa chính nhiều cột. MembershipPeriod
bảng có khóa ngoại tham chiếu đến khóa chính nhiều cột đó. Do đó, cả định nghĩa khóa chính và khóa ngoại đều bao gồm các cột được phân tách bằng dấu phẩy.
Lý do đằng sau thiết kế cơ sở dữ liệu trên là một nhạc sĩ có thể là thành viên của nhiều ban nhạc. Ngoài ra, mỗi ban nhạc có thể có nhiều nhạc sĩ. Vì vậy, chúng tôi có một mối quan hệ nhiều-nhiều. Đây là lý do tại sao BandMember
bảng được tạo - nó được sử dụng làm bảng tham chiếu chéo giữa Musician
bảng và Band
bàn. Trong trường hợp này, tôi đã chọn sử dụng khóa chính tổng hợp.
Nhưng một nhạc sĩ cũng có thể là thành viên của một ban nhạc trong nhiều trường hợp (ví dụ:một nhạc sĩ có thể rời ban nhạc, chỉ để trở lại sau). Do đó, MembershipPeriod
bảng có thể được sử dụng để ghi lại tất cả các thời kỳ mà mỗi nhạc sĩ đã từng là thành viên của mỗi ban nhạc. Điều này cần tham chiếu đến khóa chính tổng hợp trên BandMember
bảng, và vì vậy tôi cần tạo một khóa ngoại nhiều cột.
Ví dụ 2 - Chèn dữ liệu
Chỉ cần chạy đoạn mã trên, bây giờ tôi có thể tải cơ sở dữ liệu với dữ liệu:
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' );
Ví dụ 3 - Truy vấn cơ bản
Dưới đây là một ví dụ về truy vấn có thể được chạy trên cơ sở dữ liệu:
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;
Kết quả:
+------------------+-------------+------------+------------+ | 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 | +------------------+-------------+------------+------------+
Vì vậy, bây giờ chúng ta có thể biết ngày nào mỗi nhạc sĩ là thành viên của mỗi ban nhạc, ngay cả khi họ là thành viên nhiều lần.
Ví dụ 4 - Truy vấn được sửa đổi một chút
Chúng tôi có thể sửa đổi truy vấn trên để cung cấp kết quả ở định dạng dễ đọc hơn một chút:
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;
Kết quả:
+------------------+-------------+------------------------------------+ | 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 | +------------------+-------------+------------------------------------+
Ví dụ này tận dụng lợi thế của STRING_AGG()
chức năng nối các khoảng thời gian khác nhau cho mỗi nhạc sĩ. Điều này giúp giảm số lượng hàng được yêu cầu và cho phép chúng tôi nhóm các khoảng thời gian lại với nhau trong cùng một trường.
Tôi cũng tận dụng ISNULL()
, cho phép tôi thay đổi bất kỳ giá trị NULL nào thành một cái gì đó có ý nghĩa hơn.
Lưu ý rằng ISNULL()
yêu cầu rằng đối số thứ hai thuộc loại có thể được chuyển đổi ngầm định thành kiểu của đối số đầu tiên. Trong trường hợp này, đối số đầu tiên ban đầu là ngày nhập, có nghĩa là tôi sẽ không thể sử dụng một chuỗi. Tuy nhiên, trong trường hợp này, tôi quyết định sử dụng FORMAT()
chức năng định dạng ngày giá trị. Hàm này chuyển đổi hoàn toàn ngày giá trị thành một chuỗi và do đó tôi có thể sử dụng một chuỗi cho đối số thứ hai.