Uma chave estrangeira composta é uma chave estrangeira que consiste em várias colunas.
Este artigo fornece um exemplo de criação de uma chave estrangeira composta usando Transact-SQL no SQL Server.
Você pode criar uma chave estrangeira composta da mesma forma que criaria uma única chave estrangeira, exceto que, em vez de especificar apenas uma coluna, você fornece o nome de duas ou mais colunas, separadas por uma vírgula.
Assim:
CONSTRAINT FK_FKName FOREIGN KEY (FKColumn1, FKColumn2) REFERENCES PrimaryKeyTable (PKColumn1, PKColumn2)
Exemplo 1 – Criar uma chave estrangeira composta
Aqui está um exemplo de um banco de dados usando uma chave estrangeira composta (e uma chave primária composta).
Para os propósitos deste exemplo, criarei um banco de dados chamado BandTest :
CREATE DATABASE BandTest;
Agora que o banco de dados foi criado, vamos em frente e criar as tabelas.
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) );
Neste exemplo, o
BandMember
tabela tem uma chave primária de várias colunas. O MembershipPeriod
table tem uma chave estrangeira que faz referência a essa chave primária de várias colunas. Portanto, as definições de chave primária e estrangeira incluem as colunas separadas por uma vírgula. O raciocínio por trás do design do banco de dados acima é que um músico poderia ser membro de muitas bandas. Além disso, cada banda pode ter muitos músicos. Portanto, temos uma relação de muitos para muitos. É por isso que o
BandMember
a tabela é criada – ela é usada como uma tabela de referência cruzada entre o Musician
tabela e a Band
tabela. Nesse caso, optei por usar uma chave primária composta. Mas um músico também pode ser membro de uma banda em mais de uma ocasião (por exemplo, um músico pode deixar uma banda, apenas para retornar mais tarde). Portanto, o
MembershipPeriod
A tabela pode ser usada para registrar todos os períodos em que cada músico foi membro de cada banda. Isso precisa fazer referência à chave primária composta no BandMember
table e, portanto, preciso criar uma chave estrangeira de várias colunas. Exemplo 2 – Inserir dados
Tendo acabado de executar o código acima, agora posso carregar o banco de dados com dados:
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' );
Exemplo 3 – Consulta básica
Aqui está um exemplo de uma consulta que pode ser executada no banco de dados:
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 | +------------------+-------------+------------+------------+
Assim, agora podemos ver em quais datas cada músico foi membro de cada banda, mesmo que tenha sido membro em várias ocasiões.
Exemplo 4 – Consulta ligeiramente modificada
Poderíamos modificar a consulta acima para fornecer os resultados em um formato um pouco mais legível:
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 exemplo tira proveito do
STRING_AGG()
função para concatenar os vários períodos de tempo para cada músico. Isso acaba reduzindo o número de linhas necessárias e nos permite agrupar os períodos de tempo no mesmo campo. Eu também aproveito o
ISNULL()
função, que me permite alterar qualquer valor NULL em algo mais significativo. Observe que
ISNULL()
requer que o segundo argumento seja de um tipo que possa ser convertido implicitamente no tipo do primeiro argumento. Nesse caso, o primeiro argumento era originalmente uma data type, o que significa que eu não seria capaz de usar uma string. No entanto, neste caso eu decidi usar o FORMAT()
função para formatar a data valor. Esta função converte implicitamente a data value para uma string e, portanto, consegui usar uma string para o segundo argumento.