O que é chave estrangeira (FK) e por que ela existe
Chave estrangeira (foreign key) é uma restrição no banco de dados que faz um atributo (ou conjunto de atributos) em uma tabela apontar para uma chave (normalmente a chave primária, mas pode ser uma chave única) de outra tabela. Ela é o mecanismo que implementa relacionamentos no nível físico e garante integridade referencial: um registro “filho” só pode referenciar um registro “pai” que realmente exista.
Em termos práticos, a FK impede que você grave valores inválidos em colunas de referência e define o que deve acontecer quando o registro referenciado é atualizado ou excluído.
Exemplo base (Cliente e Pedido)
CLIENTE(id_cliente PK, nome, ...)PEDIDO(id_pedido PK, id_cliente FK, data_pedido, ...)A regra de integridade referencial aqui é: PEDIDO.id_cliente deve existir em CLIENTE.id_cliente (ou ser nulo, se o relacionamento permitir).
Como a FK garante integridade referencial
- Na inserção: impede inserir um filho apontando para um pai inexistente.
- Na atualização do valor da FK: impede trocar a referência para um pai inexistente.
- Na exclusão do pai: impede (ou trata) a exclusão quando existem filhos dependentes.
- Na atualização da chave do pai (menos comum, mas possível): impede (ou trata) a atualização quando existem filhos referenciando.
Esses comportamentos são controlados por ações ON DELETE e ON UPDATE na definição da FK.
Passo a passo prático: definindo uma FK com ações
1) Confirme a coluna referenciada
A coluna no “pai” precisa ser chave primária ou ter restrição de unicidade (por exemplo, UNIQUE). Exemplo: CLIENTE.id_cliente é PK.
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
2) Defina o domínio compatível
O tipo e o domínio do campo FK devem ser compatíveis com o campo referenciado (mesmo tipo, tamanho e semântica). Ex.: ambos INT ou ambos UUID.
3) Decida se a FK pode ser nula
Se o relacionamento for opcional do lado do filho, a coluna FK pode aceitar NULL. Se for obrigatório, use NOT NULL.
4) Escolha as ações de integridade (DELETE/UPDATE)
As ações devem refletir a regra de negócio: o que acontece com os filhos quando o pai muda ou é removido.
5) Crie a restrição
ALTER TABLE PEDIDO ADD CONSTRAINT fk_pedido_cliente FOREIGN KEY (id_cliente) REFERENCES CLIENTE(id_cliente) ON DELETE RESTRICT ON UPDATE RESTRICT;O exemplo acima impede excluir/alterar um cliente se houver pedidos associados.
Comportamentos em operações: RESTRICT/NO ACTION, CASCADE, SET NULL, SET DEFAULT
As ações abaixo podem ser usadas em ON DELETE e ON UPDATE. A escolha não é “técnica”, é uma decisão de regra de negócio e de preservação de histórico.
| Ação | O que faz | Quando faz sentido | Riscos/atenções |
|---|---|---|---|
RESTRICT / NO ACTION | Bloqueia a operação no pai se existirem filhos referenciando. | Quando o filho não pode existir sem o pai e você não quer apagar/alterar automaticamente. Ex.: não permitir excluir CLIENTE com PEDIDO. | Exige fluxo de negócio explícito: primeiro tratar filhos (cancelar, arquivar, transferir), depois excluir/alterar pai. |
CASCADE | Propaga a exclusão/atualização do pai para os filhos. | Quando o filho é estritamente dependente e não tem significado sem o pai. Ex.: ITEM_PEDIDO deve ser removido ao excluir PEDIDO. | Risco de apagar/alterar grandes volumes sem perceber. Pode causar “efeito dominó” em cadeias de FKs. |
SET NULL | Ao excluir/alterar o pai, define a FK do filho como NULL. | Quando o relacionamento é opcional e o filho pode continuar existindo sem o pai. Ex.: TAREFA pode ficar sem RESPONSAVEL. | A coluna FK precisa aceitar NULL. Pode gerar registros “sem dono” que exigem tratamento (fila de reatribuição). |
SET DEFAULT | Ao excluir/alterar o pai, define a FK do filho para um valor padrão. | Quando existe um “pai padrão” válido. Ex.: reatribuir para CLIENTE “Não identificado” ou USUARIO “Sistema”. | O valor default deve existir no pai; caso contrário, a operação falha. Pode mascarar problemas se usado sem critério. |
RESTRICT vs NO ACTION (nota prática)
Em muitos SGBDs, RESTRICT e NO ACTION se comportam de forma muito semelhante. A diferença costuma aparecer em cenários de verificação adiada (deferred constraints) ou no momento exato da checagem dentro da transação. Como regra geral de modelagem, trate ambos como “não deixe fazer se houver dependentes”, e valide o comportamento específico do seu SGBD quando usar transações complexas.
Entidades dependentes e consequências para exclusão/atualização
Uma entidade dependente é aquela cuja existência está amarrada a outra. No banco, isso normalmente se manifesta por uma FK obrigatória (NOT NULL) e, em muitos casos, por uma estratégia de exclusão/atualização coerente com essa dependência.
Exemplo 1: Pedido e Item do Pedido (dependência forte)
PEDIDO(id_pedido PK, ...)ITEM_PEDIDO(id_pedido FK, nro_item, produto, qtd, ...)Regra típica: não existe item sem pedido. Aqui, ON DELETE CASCADE em ITEM_PEDIDO costuma fazer sentido, porque excluir um pedido deve remover seus itens.
ALTER TABLE ITEM_PEDIDO ADD CONSTRAINT fk_item_pedido FOREIGN KEY (id_pedido) REFERENCES PEDIDO(id_pedido) ON DELETE CASCADE ON UPDATE RESTRICT;Por que não CASCADE no UPDATE? Em geral, chaves primárias não deveriam mudar. Se mudarem, CASCADE no update pode propagar alterações amplas. Muitas equipes preferem impedir (RESTRICT) e tratar como operação excepcional.
Exemplo 2: Usuário e Tarefa (dependência fraca/opcional)
USUARIO(id_usuario PK, ...)TAREFA(id_tarefa PK, id_responsavel FK NULL, ...)Se ao excluir um usuário as tarefas devem continuar existindo para reatribuição, use SET NULL:
ALTER TABLE TAREFA ADD CONSTRAINT fk_tarefa_usuario FOREIGN KEY (id_responsavel) REFERENCES USUARIO(id_usuario) ON DELETE SET NULL ON UPDATE RESTRICT;Exemplo 3: “Pai padrão” (SET DEFAULT)
Suponha que pedidos possam existir associados a um cliente “genérico” (por exemplo, vendas balcão). Você pode definir id_cliente com default para o cliente “Balcão” (que deve existir em CLIENTE).
-- CLIENTE(0, 'Balcão') existe previamenteALTER TABLE PEDIDO ALTER COLUMN id_cliente SET DEFAULT 0;ALTER TABLE PEDIDO ADD CONSTRAINT fk_pedido_cliente FOREIGN KEY (id_cliente) REFERENCES CLIENTE(id_cliente) ON DELETE SET DEFAULT ON UPDATE RESTRICT;Esse padrão é útil quando a regra de negócio exige manter o registro do filho sempre associado a algum pai válido, mesmo após exclusões.
Erros comuns e como preveni-los
1) Registros órfãos (filhos sem pai)
Como acontece: ausência de FK, ou FK desabilitada, ou carga de dados “na mão” sem validação. Ex.: inserir PEDIDO.id_cliente = 999 sem existir CLIENTE(999).
Como prevenir:
- Crie FKs em todas as colunas de referência reais.
- Evite “validar na aplicação” como único mecanismo; o banco deve garantir.
- Em migrações/cargas, carregue primeiro os pais e depois os filhos.
Como detectar (consulta típica):
SELECT p.* FROM PEDIDO p LEFT JOIN CLIENTE c ON c.id_cliente = p.id_cliente WHERE c.id_cliente IS NULL;2) Inconsistência por ação inadequada (CASCADE perigoso)
Como acontece: usar ON DELETE CASCADE em relacionamentos que deveriam preservar histórico. Ex.: excluir CLIENTE e apagar automaticamente PEDIDO, perdendo rastreabilidade.
Como prevenir:
- Para entidades de histórico (pedido, nota, pagamento), prefira
RESTRICT/NO ACTIONe trate exclusão como “inativação” (quando aplicável ao domínio). - Se CASCADE for necessário, limite-o a dependências claramente “componentes” (ex.: itens do pedido).
- Mapeie cadeias de FKs para evitar cascatas em múltiplos níveis sem intenção.
3) Usar SET NULL em coluna NOT NULL
Como acontece: definir ON DELETE SET NULL mas a coluna FK foi criada como NOT NULL. Resultado: a exclusão do pai falha.
Como prevenir:
- Alinhe opcionalidade: se a ação é
SET NULL, a coluna deve permitirNULL. - Valide a regra: o filho pode existir sem pai? Se não, não use
SET NULL.
4) SET DEFAULT sem “pai default” válido
Como acontece: definir SET DEFAULT mas o default não existe na tabela pai, ou o default não foi definido na coluna.
Como prevenir:
- Crie e proteja o registro “default” no pai (e.g., cliente balcão).
- Garanta que o default esteja definido na coluna FK e seja compatível com o domínio.
5) Referenciar coluna não única no pai
Como acontece: tentar criar FK apontando para uma coluna que não é PK nem UNIQUE. Isso quebra a garantia de “um pai identificável”.
Como prevenir:
- Referencie PK ou uma chave alternativa com
UNIQUE. - Se a regra de negócio pede referência por um identificador natural, imponha unicidade nele antes de referenciar.
6) Atualização de chave do pai sem estratégia (ON UPDATE)
Como acontece: permitir que o valor referenciado mude sem definir ON UPDATE adequado, causando falha na atualização ou inconsistência se a FK não existir.
Como prevenir:
- Evite atualizar chaves primárias; trate como imutáveis sempre que possível.
- Se a atualização for necessária (ex.: chave natural que pode mudar), avalie
ON UPDATE CASCADEcom cautela e testes. - Prefira chaves substitutas para reduzir a necessidade de updates em chaves referenciadas.
Checklist rápido para escolher a ação correta
- Filho não pode existir sem pai: FK
NOT NULLe geralmenteON DELETE RESTRICT(se quer bloquear) ouON DELETE CASCADE(se quer apagar junto). - Filho pode existir sem pai: FK permite
NULLeON DELETE SET NULL. - Filho deve sempre apontar para algum pai válido, mesmo após exclusão:
ON DELETE SET DEFAULTcom “pai padrão” existente. - Preservar histórico: evite cascata do pai para entidades históricas; prefira bloquear e tratar por processo.
- Chave do pai não deve mudar:
ON UPDATE RESTRICT/NO ACTION.