Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Como criar uma chave estrangeira composta no SQL Server (exemplo T-SQL)


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.