複合外部キー 複数の列で構成される外部キーです。
この記事では、SQLServerでTransact-SQLを使用して複合外部キーを作成する例を示します。
単一の外部キーを作成するのと同じように複合外部キーを作成できますが、1つの列だけを指定する代わりに、2つ以上の列の名前をコンマで区切って指定する点が異なります。
このように:
CONSTRAINT FK_FKName FOREIGN KEY (FKColumn1, FKColumn2) REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)
例1-複合外部キーを作成する
これは、複合外部キー(および複合主キー)を使用するデータベースの例です。
この例では、 BandTest というデータベースを作成します。 :
CREATE DATABASE BandTest;
データベースが作成されたので、先に進んでテーブルを作成しましょう。
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) );
この例では、BandMember
テーブルには複数列の主キーがあります。 MembershipPeriod
テーブルには、その複数列の主キーを参照する外部キーがあります。したがって、主キー定義と外部キー定義の両方に、コンマで区切られた列が含まれます。
上記のデータベース設計の背後にある理由は、ミュージシャンが多くのバンドのメンバーになる可能性があるということです。また、各バンドには多くのミュージシャンがいる可能性があります。したがって、私たちは多対多の関係を持っています。これがBandMember
の理由です テーブルが作成されます–Musician
間の相互参照テーブルとして使用されます テーブルとBand
テーブル。この場合、複合主キーを使用することを選択しました。
ただし、ミュージシャンは複数回バンドのメンバーになることもできます(たとえば、ミュージシャンがバンドを離れて、後で戻る場合があります)。したがって、MembershipPeriod
テーブルを使用して、各ミュージシャンが各バンドのメンバーであったすべての期間を記録できます。これは、BandMember
の複合主キーを参照する必要があります テーブルなので、複数列の外部キーを作成する必要があります。
例2–データの挿入
上記のコードを実行しただけで、データベースにデータをロードできます。
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' );
例3–基本的なクエリ
データベースに対して実行できるクエリの例を次に示します。
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;
結果:
+------------------+-------------+------------+------------+ | 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 | +------------------+-------------+------------+------------+
これで、複数の機会にメンバーであった場合でも、各ミュージシャンが各バンドのメンバーであった日付を確認できます。
例4–わずかに変更されたクエリ
上記のクエリを変更して、結果をもう少し読みやすい形式で提供することができます。
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;
結果:
+------------------+-------------+------------------------------------+ | 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 | +------------------+-------------+------------------------------------+
この例では、STRING_AGG()
を利用しています。 各ミュージシャンのさまざまな期間を連結する機能。これにより、必要な行数が減り、同じフィールドに期間をグループ化できるようになります。
ISNULL()
も利用しています 関数。これにより、NULL値をより意味のあるものに変更できます。
ISNULL()
に注意してください 2番目の引数は、最初の引数の型に暗黙的に変換できる型である必要があります。この場合、最初の引数は元々日付でした タイプ。これは、文字列を使用できないことを意味します。ただし、この場合、FORMAT()
を使用することにしました。 日付をフォーマットする関数 価値。この関数は、日付を暗黙的に変換します 文字列の値であるため、2番目の引数に文字列を使用できました。