Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Come creare una chiave esterna composita in SQL Server (esempio T-SQL)

Una chiave esterna composita è una chiave esterna composta da più colonne.

Questo articolo fornisce un esempio di creazione di una chiave esterna composita usando Transact-SQL in SQL Server.

Puoi creare una chiave esterna composita proprio come faresti con una singola chiave esterna, tranne per il fatto che invece di specificare una sola colonna, fornisci il nome di due o più colonne, separate da una virgola.

In questo modo:

CONSTRAINT FK_FKName FOREIGN KEY
 (FKColumn1, FKColumn2)
REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)

Esempio 1:creare una chiave esterna composita

Ecco un esempio di database che utilizza una chiave esterna composita (e una chiave primaria composita).

Ai fini di questo esempio, creerò un database chiamato BandTest :

CREATE DATABASE BandTest;

Ora che il database è stato creato, andiamo avanti e creiamo le tabelle.

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 questo esempio, il BandMember la tabella ha una chiave primaria multicolonna. Il MembershipPeriod la tabella ha una chiave esterna che fa riferimento a quella chiave primaria multicolonna. Pertanto, sia la definizione di chiave primaria che quella esterna includono le colonne separate da una virgola.

Il ragionamento alla base della progettazione del database di cui sopra è che un musicista potrebbe potenzialmente essere un membro di molte band. Inoltre, ogni band può avere molti musicisti. Quindi abbiamo una relazione molti-a-molti. Ecco perché il BandMember viene creata la tabella:viene utilizzata come tabella di riferimento incrociato tra il Musician tabella e la Band tavolo. In questo caso, ho scelto di utilizzare una chiave primaria composita.

Ma un musicista potrebbe anche essere membro di una band in più di un'occasione (ad esempio un musicista potrebbe lasciare una band, solo per tornare più tardi). Pertanto, il MembershipPeriod tabella può essere utilizzata per registrare tutti i periodi in cui ogni musicista è stato membro di ciascuna band. Questo deve fare riferimento alla chiave primaria composita su BandMember tabella, quindi ho bisogno di creare una chiave esterna multicolonna.

Esempio 2 – Inserisci dati

Dopo aver appena eseguito il codice sopra, ora posso caricare il database con i dati:

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' );

Esempio 3 – Query di base

Ecco un esempio di una query che potrebbe essere eseguita sul database:

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;

Risultato:

+------------------+-------------+------------+------------+
| 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 |
+------------------+-------------+------------+------------+

Quindi ora possiamo vedere in quali date ogni musicista è stato membro di ciascuna band, anche se è stato membro in più occasioni.

Esempio 4 – Query leggermente modificata

Potremmo modificare la query precedente per fornire i risultati in un formato leggermente più leggibile:

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;

Risultato:

+------------------+-------------+------------------------------------+
| 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                          |
+------------------+-------------+------------------------------------+

Questo esempio sfrutta il STRING_AGG() funzione per concatenare i vari periodi temporali per ogni musicista. Questo finisce per ridurre il numero di righe richieste e ci consente di raggruppare i periodi di tempo nello stesso campo.

Approfitto anche di ISNULL() funzione, che mi consente di modificare qualsiasi valore NULL in qualcosa di più significativo.

Nota che ISNULL() richiede che il secondo argomento sia di un tipo che può essere convertito in modo implicito nel tipo del primo argomento. In questo caso, il primo argomento era originariamente una data tipo, il che significa che non sarei in grado di utilizzare una stringa. Tuttavia, in questo caso ho deciso di utilizzare il FORMAT() funzione per formattare la data valore. Questa funzione converte implicitamente la data valore a una stringa, e quindi sono stato in grado di utilizzare una stringa per il secondo argomento.