Curso SQL Server — Edição Definitiva

Módulo 10 — FOREIGN KEY (integridade referencial): 1‑N, N‑N, CASCADE, SET NULL e boas práticas

Aprenda a relacionar tabelas com FOREIGN KEY garantindo integridade referencial. Veja 1‑para‑N, tabelas de junção (N‑para‑N), ações de cascata e como indexar corretamente.

Objetivo

Entender FOREIGN KEY: como declarar, quando usar, impactos em INSERT/UPDATE/DELETE, opções de CASCADE e SET NULL, além de práticas para desempenho e manutenção.

A FK garante que um filho só exista se o pai existir. Sem FK, você pode acumular “linhas órfãs”.

FOREIGN KEY (inline na coluna)

O que é / Para que serve: Declarar FK na própria coluna que referencia a PK (ou UNIQUE) da tabela pai.

Sintaxe: Coluna TIPO REFERENCES Pai(ChavePai)

Como funciona: O SQL impede inserir valores na coluna que não existam na tabela pai e impede excluir o pai com filhos (salvo ação configurada).

Implicações & boas práticas: • A coluna referenciada deve ser PK ou UNIQUE. • Garanta tipos compatíveis. • Indexe a FK para acelerar JOINs e deletes do pai.

CREATE TABLE dbo.Clientes (
  ClienteID INT PRIMARY KEY,
  Nome NVARCHAR(100) NOT NULL
);
CREATE TABLE dbo.Pedidos (
  PedidoID INT PRIMARY KEY,
  ClienteID INT NOT NULL REFERENCES dbo.Clientes(ClienteID),
  CriadoEm DATETIME2(3) NOT NULL DEFAULT(SYSDATETIME())
);
-- Falha: não existe cliente 99
-- INSERT INTO dbo.Pedidos (PedidoID,ClienteID) VALUES (1,99);

FOREIGN KEY (constraint nomeada)

O que é / Para que serve: Dar nome à constraint e configurar ação ON DELETE/UPDATE.

Sintaxe: CONSTRAINT FK_Filhos_Pai FOREIGN KEY (ColFilho) REFERENCES Pai(ChavePai) [ON DELETE ...] [ON UPDATE ...]

Como funciona: O nome facilita manutenção. Você pode definir ações ao excluir/atualizar o pai.

Implicações & boas práticas: • Padrões: ON DELETE NO ACTION (bloqueia se houver filhos). • Outras opções: CASCADE e SET NULL.

CREATE TABLE dbo.Pedidos (
  PedidoID INT PRIMARY KEY,
  ClienteID INT NOT NULL,
  CONSTRAINT FK_Pedidos_Clientes
    FOREIGN KEY (ClienteID)
    REFERENCES dbo.Clientes(ClienteID)
    ON DELETE NO ACTION -- padrão
    ON UPDATE NO ACTION
);

ON DELETE/UPDATE CASCADE

O que é / Para que serve: Propagar exclusão/atualização do pai para os filhos automaticamente.

Sintaxe: FOREIGN KEY (...) REFERENCES Pai(...) ON DELETE CASCADE ON UPDATE CASCADE

Como funciona: Ao excluir o pai, todos os filhos são excluídos; ao atualizar a chave do pai, reflete nos filhos.

Implicações & boas práticas: • Use com cuidado: um DELETE no pai pode excluir muitas linhas. • Boa para dados “dependentes” sem vida própria (ex.: itens do pedido).

CREATE TABLE dbo.Pedidos (
  PedidoID INT PRIMARY KEY,
  ClienteID INT NOT NULL,
  CONSTRAINT FK_Pedidos_Clientes
    FOREIGN KEY (ClienteID)
    REFERENCES dbo.Clientes(ClienteID)
    ON DELETE CASCADE
);
-- Excluir cliente apaga seus pedidos
-- DELETE FROM dbo.Clientes WHERE ClienteID = 1;

ON DELETE SET NULL

O que é / Para que serve: Quando o pai é excluído, a FK do filho vira NULL (se permitido).

Sintaxe: FOREIGN KEY (...) REFERENCES Pai(...) ON DELETE SET NULL

Como funciona: Preserva o registro do filho, mas sem vínculo com o pai (FK deve aceitar NULL).

Implicações & boas práticas: • Bom quando o filho pode sobreviver sem pai. • Cuidado com relatórios que assumem vínculo obrigatório.

CREATE TABLE dbo.Orcamentos (
  OrcamentoID INT PRIMARY KEY,
  ClienteID INT NULL,
  CONSTRAINT FK_Orcamentos_Clientes
    FOREIGN KEY (ClienteID)
    REFERENCES dbo.Clientes(ClienteID)
    ON DELETE SET NULL
);

N‑para‑N (tabela de junção)

O que é / Para que serve: Relacionamento muitos‑para‑muitos via tabela auxiliar com duas FKs.

Sintaxe: CREATE TABLE Junção (AId INT, BId INT, PRIMARY KEY(AId,BId), FK→A, FK→B)

Como funciona: A tabela de junção guarda os pares; a PK composta evita duplicatas.

Implicações & boas práticas: • Indexe as duas colunas. • Considere ON DELETE CASCADE para limpar pares automaticamente.

CREATE TABLE dbo.Alunos (
  AlunoID INT PRIMARY KEY, Nome NVARCHAR(100) NOT NULL
);
CREATE TABLE dbo.Cursos (
  CursoID INT PRIMARY KEY, Titulo NVARCHAR(100) NOT NULL
);
CREATE TABLE dbo.AlunoCurso (
  AlunoID INT NOT NULL,
  CursoID INT NOT NULL,
  CONSTRAINT PK_AlunoCurso PRIMARY KEY (AlunoID, CursoID),
  CONSTRAINT FK_AlunoCurso_Alunos FOREIGN KEY (AlunoID) REFERENCES dbo.Alunos(AlunoID) ON DELETE CASCADE,
  CONSTRAINT FK_AlunoCurso_Cursos FOREIGN KEY (CursoID) REFERENCES dbo.Cursos(CursoID) ON DELETE CASCADE
);

Índices em FKs (desempenho)

Crie índice na coluna filha da FK para acelerar JOINs e exclusões do pai.

CREATE INDEX IX_Pedidos_ClienteID ON dbo.Pedidos(ClienteID);
Sem índice, excluir um pai pode exigir table scan na tabela filha para checar filhos — lento em grandes volumes.

Habilitar/Desabilitar verificação (migrações)

Você pode desabilitar a checagem temporariamente (não recomendado em produção), inserir/migrar e reabilitar verificando todos os dados.

-- Desabilitar verificação (aceita dados possivelmente inválidos)
ALTER TABLE dbo.Pedidos NOCHECK CONSTRAINT FK_Pedidos_Clientes;
GO
-- Reabilitar e checar todas as linhas (recomendado)
ALTER TABLE dbo.Pedidos WITH CHECK CHECK CONSTRAINT FK_Pedidos_Clientes;
Atenção: Desabilitar e reabilitar sem WITH CHECK deixa linhas “não confiáveis” fora da verificação. Evite em produção.

Erros comuns & soluções

Exercícios práticos

  1. Crie Clientes e Pedidos com FK tradicional (NO ACTION). Tente excluir um cliente com pedidos e observe o erro.
  2. Recrie a FK com ON DELETE CASCADE e teste a exclusão do cliente.
  3. Crie Orcamentos com ON DELETE SET NULL e demonstre a nulidade após excluir o cliente.
  4. Implemente o relacionamento N‑para‑N com AlunoCurso e insira pares (Aluno,Curso). Teste ON DELETE CASCADE.
  5. Crie o índice IX_Pedidos_ClienteID e compare plano de execução em um JOIN com/sem índice.

Referências oficiais & leituras recomendadas