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.