FOREIGN KEY: relacionando tabelas

1. O que é uma FOREIGN KEY e por que ela existe?

Uma FOREIGN KEY (chave estrangeira) é um mecanismo fundamental em bancos de dados relacionais que estabelece um vínculo entre duas tabelas. Conceitualmente, funciona como uma "ponte" que conecta registros de uma tabela (tabela filha) a registros de outra tabela (tabela pai), garantindo que os dados relacionados existam e sejam consistentes.

A principal função da FOREIGN KEY é assegurar a integridade referencial dos dados. Isso significa que, se uma coluna em uma tabela referencia um valor existente em outra tabela, o banco de dados não permitirá que esse valor referenciado seja excluído ou alterado de forma que quebre o relacionamento.

A diferença fundamental entre chave primária (PRIMARY KEY) e chave estrangeira é:

  • PRIMARY KEY: identifica unicamente cada registro em uma tabela. Não permite valores nulos ou duplicados.
  • FOREIGN KEY: referencia a chave primária de outra tabela. Pode conter valores nulos e duplicados, dependendo da regra de negócio.

2. Sintaxe básica para criar FOREIGN KEY

Criando FOREIGN KEY na criação da tabela

CREATE TABLE clientes (
    id_cliente INT PRIMARY KEY,
    nome VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE
);

CREATE TABLE pedidos (
    id_pedido INT PRIMARY KEY,
    data_pedido DATE NOT NULL,
    valor_total DECIMAL(10,2),
    id_cliente INT,
    FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
);

Adicionando FOREIGN KEY em tabela existente

ALTER TABLE pedidos
ADD CONSTRAINT fk_pedidos_clientes
FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente);

A sintaxe completa segue o padrão: FOREIGN KEY (coluna_filha) REFERENCES tabela_pai(coluna_pai). A constraint pode receber um nome personalizado (como fk_pedidos_clientes) para facilitar futuras manutenções.

3. Regras de integridade referencial: ON DELETE e ON UPDATE

As cláusulas ON DELETE e ON UPDATE definem o comportamento do banco de dados quando um registro pai é excluído ou atualizado.

ON DELETE CASCADE

Remove automaticamente todos os registros filhos quando o registro pai é excluído.

CREATE TABLE pedidos (
    id_pedido INT PRIMARY KEY,
    id_cliente INT,
    FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
    ON DELETE CASCADE
);

ON DELETE SET NULL

Define a coluna FK como NULL quando o registro pai é excluído, mantendo o registro filho órfão.

FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
ON DELETE SET NULL

ON DELETE RESTRICT e NO ACTION

Impedem a exclusão do registro pai se existirem registros filhos relacionados. A diferença entre eles é sutil e depende do SGBD: RESTRICT verifica imediatamente, enquanto NO ACTION pode adiar a verificação.

FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
ON DELETE RESTRICT

Os mesmos comportamentos se aplicam ao ON UPDATE, controlando o que acontece quando a chave primária do registro pai é alterada.

4. Exemplo prático: modelando um sistema de pedidos

Vamos construir um sistema completo de pedidos com integridade referencial:

-- Criação da tabela clientes
CREATE TABLE clientes (
    id_cliente INT AUTO_INCREMENT PRIMARY KEY,
    nome VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    data_cadastro DATE DEFAULT CURRENT_DATE
);

-- Criação da tabela pedidos com FOREIGN KEY
CREATE TABLE pedidos (
    id_pedido INT AUTO_INCREMENT PRIMARY KEY,
    data_pedido DATETIME DEFAULT CURRENT_TIMESTAMP,
    valor_total DECIMAL(10,2) NOT NULL,
    id_cliente INT NOT NULL,
    FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente)
    ON DELETE RESTRICT
);

-- Inserindo dados válidos
INSERT INTO clientes (nome, email) VALUES
('Maria Silva', 'maria@email.com'),
('João Santos', 'joao@email.com');

INSERT INTO pedidos (valor_total, id_cliente) VALUES
(150.00, 1),
(89.90, 2);

-- Tentativa de inserir pedido com cliente inexistente (ERRO!)
INSERT INTO pedidos (valor_total, id_cliente) VALUES (200.00, 99);
-- Erro: Cannot add or update a child row: a foreign key constraint fails

O banco de dados rejeitará a inserção do pedido com id_cliente = 99 porque não existe nenhum cliente com esse identificador, protegendo a integridade dos dados.

5. Relacionamentos mais complexos com múltiplas FOREIGN KEYs

FK composta referenciando chave primária composta

CREATE TABLE turmas (
    id_curso INT,
    id_turma INT,
    PRIMARY KEY (id_curso, id_turma)
);

CREATE TABLE matriculas (
    id_matricula INT PRIMARY KEY,
    id_curso INT,
    id_turma INT,
    id_aluno INT,
    FOREIGN KEY (id_curso, id_turma) REFERENCES turmas(id_curso, id_turma)
);

Auto-referência (hierarquia de funcionários)

CREATE TABLE funcionarios (
    id_funcionario INT PRIMARY KEY,
    nome VARCHAR(100) NOT NULL,
    id_gestor INT,
    FOREIGN KEY (id_gestor) REFERENCES funcionarios(id_funcionario)
);

Tabela associativa para relacionamento N:N

CREATE TABLE alunos (
    id_aluno INT PRIMARY KEY,
    nome VARCHAR(100)
);

CREATE TABLE cursos (
    id_curso INT PRIMARY KEY,
    nome_curso VARCHAR(100)
);

CREATE TABLE alunos_cursos (
    id_aluno INT,
    id_curso INT,
    data_matricula DATE,
    PRIMARY KEY (id_aluno, id_curso),
    FOREIGN KEY (id_aluno) REFERENCES alunos(id_aluno),
    FOREIGN KEY (id_curso) REFERENCES cursos(id_curso)
);

6. Boas práticas e armadilhas comuns

Indexação de FOREIGN KEY: A maioria dos SGBDs não cria índices automaticamente nas colunas FK. É recomendável criá-los manualmente para melhorar performance em JOINs:

CREATE INDEX idx_pedidos_cliente ON pedidos(id_cliente);

Cuidado com cascatas infinitas: Em tabelas com auto-referência ou relacionamentos circulares, CASCADE pode criar loops infinitos. Sempre teste cenários de exclusão antes de implementar em produção.

Evitar FK em colunas voláteis: Prefira chaves primárias estáveis (como IDs numéricos) em vez de colunas que mudam com frequência (como CPF ou email), pois alterações na PK pai podem propagar atualizações indesejadas.

Documentação: Mantenha um diagrama ou documentação textual dos relacionamentos. Em projetos complexos, isso economiza horas de debugging.

7. Consultando dados entre tabelas com FOREIGN KEY

INNER JOIN usando FK

SELECT c.nome, p.id_pedido, p.valor_total
FROM clientes c
INNER JOIN pedidos p ON c.id_cliente = p.id_cliente;

LEFT JOIN para encontrar registros órfãos

SELECT p.id_pedido, p.id_cliente
FROM pedidos p
LEFT JOIN clientes c ON p.id_cliente = c.id_cliente
WHERE c.id_cliente IS NULL;

Verificando integridade referencial

-- Encontrar pedidos sem cliente correspondente
SELECT * FROM pedidos
WHERE id_cliente NOT IN (SELECT id_cliente FROM clientes);

8. Gerenciando FOREIGN KEY no dia a dia

Removendo uma FOREIGN KEY

ALTER TABLE pedidos DROP FOREIGN KEY fk_pedidos_clientes;

Desabilitando verificações temporariamente (MySQL)

SET FOREIGN_KEY_CHECKS = 0;
-- Operações que violariam integridade
SET FOREIGN_KEY_CHECKS = 1;

Risco: Desabilitar verificações pode corromper a integridade dos dados. Use apenas em migrações controladas e sempre restaure as verificações imediatamente.

Adicionando FK em tabela com dados existentes

Antes de adicionar a constraint, limpe dados inconsistentes:

-- Identificar registros problemáticos
DELETE FROM pedidos WHERE id_cliente NOT IN (SELECT id_cliente FROM clientes);

-- Agora é seguro adicionar a FK
ALTER TABLE pedidos
ADD CONSTRAINT fk_pedidos_clientes
FOREIGN KEY (id_cliente) REFERENCES clientes(id_cliente);

As FOREIGN KEYs são a espinha dorsal da integridade referencial em bancos de dados relacionais. Dominar sua sintaxe, comportamentos e armadilhas é essencial para construir sistemas robustos e confiáveis. Lembre-se: uma FK bem projetada previne inconsistências antes que elas aconteçam, economizando horas de correção de dados no futuro.

Referências