sql >> Base de Datos >  >> RDS >> Sqlserver

Cómo crear una clave externa compuesta en SQL Server (ejemplo de T-SQL)

Una clave foránea compuesta es una clave externa que consta de varias columnas.

Este artículo proporciona un ejemplo de cómo crear una clave externa compuesta mediante Transact-SQL en SQL Server.

Puede crear una clave externa compuesta de la misma manera que crearía una clave externa única, excepto que en lugar de especificar solo una columna, proporcione el nombre de dos o más columnas, separadas por una coma.

Así:

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

Ejemplo 1:crear una clave externa compuesta

Este es un ejemplo de una base de datos que utiliza una clave externa compuesta (y una clave principal compuesta).

Para los propósitos de este ejemplo, crearé una base de datos llamada BandTest :

CREATE DATABASE BandTest;

Ahora que se creó la base de datos, sigamos adelante y creemos las tablas.

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

En este ejemplo, el BandMember la tabla tiene una clave principal de varias columnas. El MembershipPeriod la tabla tiene una clave externa que hace referencia a esa clave principal de varias columnas. Por lo tanto, las definiciones de clave principal y externa incluyen las columnas separadas por una coma.

El razonamiento detrás del diseño de la base de datos anterior es que un músico podría ser miembro de muchas bandas. Además, cada banda puede tener muchos músicos. Así que tenemos una relación de muchos a muchos. Esta es la razón por la que BandMember se crea la tabla:se utiliza como tabla de referencias cruzadas entre el Musician tabla y la Band mesa. En este caso, he optado por utilizar una clave primaria compuesta.

Pero un músico también puede ser miembro de una banda en más de una ocasión (por ejemplo, un músico puede dejar una banda y regresar más tarde). Por lo tanto, el MembershipPeriod La tabla se puede utilizar para registrar todos los períodos que cada músico ha sido miembro de cada banda. Esto debe hacer referencia a la clave principal compuesta en el BandMember table, por lo que necesito crear una clave externa de varias columnas.

Ejemplo 2:Insertar datos

Después de ejecutar el código anterior, ahora puedo cargar la base de datos con datos:

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

Ejemplo 3:consulta básica

Este es un ejemplo de una consulta que podría ejecutarse en la base de datos:

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;

Resultado:

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

Así que ahora podemos ver en qué fechas cada músico fue miembro de cada banda, incluso si fueron miembros en varias ocasiones.

Ejemplo 4:consulta ligeramente modificada

Podríamos modificar la consulta anterior para proporcionar los resultados en un formato un poco más legible:

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;

Resultado:

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

Este ejemplo aprovecha el STRING_AGG() para concatenar los distintos periodos de tiempo de cada músico. Esto termina reduciendo la cantidad de filas requeridas y nos permite agrupar los períodos de tiempo en el mismo campo.

También aprovecho el ISNULL() función, que me permite cambiar cualquier valor NULL en algo más significativo.

Tenga en cuenta que ISNULL() requiere que el segundo argumento sea de un tipo que pueda convertirse implícitamente al tipo del primer argumento. En este caso, el primer argumento fue originalmente una fecha type, lo que significa que no podría usar una cadena. Sin embargo, en este caso decidí usar el FORMAT() función para formatear la fecha valor. Esta función convierte implícitamente la fecha valor a una cadena y, por lo tanto, pude usar una cadena para el segundo argumento.