Chaves Estrangeiras Ausentes
Linhas órfãs e falhas de integridade aplicadas pela aplicação ocorrem quando colunas *_id não possuem REFERENCES - o banco de dados não consegue rejeitar relacionamentos impossíveis.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
-- Cheiro: coluna _id sem FK
CREATE TABLE comments (
comment_id bigint PRIMARY KEY,
post_id bigint NOT NULL
);
-- Correção: adicionar FK (após limpar órfãos)
DELETE FROM comments c
WHERE NOT EXISTS (SELECT 1 FROM posts p WHERE p.post_id = c.post_id);
ALTER TABLE comments
ADD CONSTRAINT comments_post_id_fkey
FOREIGN KEY (post_id) REFERENCES posts (post_id) ON DELETE CASCADE
NOT VALID;
ALTER TABLE comments VALIDATE CONSTRAINT comments_post_id_fkey;Quando usar isto:
- Novas tabelas com colunas com sufixo
*_id. - Limpeza pós-incidente após falha de integridade apenas pela aplicação.
- Importações de dados de CSV sem verificações de referência.
Exemplo de Trabalho
BEGIN;
CREATE TABLE authors (author_id bigint PRIMARY KEY, name text NOT NULL);
CREATE TABLE posts (
post_id bigint PRIMARY KEY,
author_id bigint NOT NULL REFERENCES authors (author_id)
);
-- Tabela filha apenas pela aplicação (defeito)
CREATE TABLE comments_bad (
comment_id bigint PRIMARY KEY,
post_id bigint NOT NULL
);
INSERT INTO posts VALUES (1, 1);
INSERT INTO comments_bad VALUES (100, 1);
INSERT INTO comments_bad VALUES (101, 999); -- órfão - sem post 999
-- Detecção
SELECT comment_id, post_id FROM comments_bad c
WHERE NOT EXISTS (SELECT 1 FROM posts p WHERE p.post_id = c.post_id);
-- Caminho de remediação
DELETE FROM comments_bad WHERE post_id = 999;
ALTER TABLE comments_bad
ADD CONSTRAINT comments_bad_post_id_fkey
FOREIGN KEY (post_id) REFERENCES posts (post_id) ON DELETE CASCADE;
-- Provar a aplicação
INSERT INTO comments_bad VALUES (102, 888); -- ERRO: violação de FK
COMMIT;O que isto demonstra:
post_id = 999órfão inserido sem erro antes da FK.- Consulta de órfãos encontra violações de integridade em massa.
- FK bloqueia novos órfãos no limite do banco de dados.
Mergulho Profundo
Como Funciona
- Restrições FK verificam a existência do pai em
INSERT/UPDATEdo filho. ON DELETEdefine o comportamento de remoção do pai (CASCADE,RESTRICT,SET NULL).NOT VALIDadiciona FK sem escanear a tabela inteira imediatamente - entãoVALIDATE CONSTRAINTescaneia com um bloqueio mais fraco em fluxos de trabalho PG 18.- Validação de aplicação perde SQL de administrador, condições de corrida e caminhos de código alternativos.
Guia ON DELETE
| Ação | Usar quando |
|---|---|
CASCADE | Filho sem significado sem pai (comentários sobre post) |
RESTRICT | Impedir a exclusão do pai se filhos existirem (linhas de fatura) |
SET NULL | Relacionamento opcional, preservar histórico do filho |
NO ACTION | Padrão - adiar a verificação para o final da transação |
Notas SQL
-- Encontrar todas as colunas nomeadas %_id sem FK (auditoria heurística)
SELECT
c.table_schema, c.table_name, c.column_name
FROM information_schema.columns c
LEFT JOIN information_schema.key_column_usage k
ON k.table_schema = c.table_schema
AND k.table_name = c.table_name
AND k.column_name = c.column_name
WHERE c.column_name LIKE '%\_id' ESCAPE '\'
AND c.table_schema = 'public';
-- Revisão manual necessária - nem todas as colunas _id são FKsArmadilhas
- FK removida "por performance" - órfãos retornam, junções retornam linhas lixo. Correção: indexar colunas FK; manter a restrição.
- Exclusão lógica de pais sem política de FK - filhos apontam para posts
excluídos. Correção:RESTRICTou sincronizar exclusão lógica na aplicação com a visão ativa. - Importação em massa desabilita triggers/FKs -
COPYcarrega órfãos. Correção: carregar em staging, validar, inserir com FKs habilitadas. - Referências entre bancos de dados - FK impossível entre bancos de dados. Correção: trabalho de validação assíncrono ou mesmo banco de dados.
- Associações polimórficas -
commentable_id+commentable_typenão podem ser uma única FK. Correção: tabelas separadas ou restrições de verificação por tipo.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Restrições FK | OLTP Padrão | Referências entre shards fragmentados |
EXCLUDE / CHECK | Regras complexas | Existência simples de pai-filho |
| Apenas Aplicação | Protótipo descartável | Dados de receita de produção |
| Trabalho periódico de órfãos | Remediação de dívida legada | Greenfield - adicionar FK imediatamente |
FAQs
Chaves estrangeiras tornam as escritas mais lentas?
Pequeno overhead por inserção - geralmente dominado pela manutenção de índices que você precisa de qualquer maneira. FKs ausentes custam mais em dados ruins e tempo de incidente.
Devo indexar todas as colunas FK?
Sim, para performance de junção e cascata. PostgreSQL não indexa colunas FK automaticamente.
Como adicionar FK a uma tabela de bilhões de linhas?
ADD CONSTRAINT ... NOT VALID e depois VALIDATE CONSTRAINT em janela de manutenção; limpe os órfãos primeiro.
Chaves FK podem referenciar chaves não primárias?
Sim - devem referenciar colunas UNIQUE ou PRIMARY KEY. UNIQUE (tenant_id, project_id) suporta FKs compostas.
E sobre FKs circulares?
Use restrições adiadas ou insira pais na mesma transação com ordenação de placeholder - raro, projete com cuidado.
FKs funcionam em tabelas particionadas?
Sim, no PostgreSQL 18 com pais/filhos particionados - planeje o design da chave incluindo colunas de partição.
Devo usar ON DELETE CASCADE em todos os lugares?
Não - cascata pode surpreender operadores que excluem uma linha pai e apagam milhares de filhos. Escolha explicitamente.
Como funcionam FKs multi-tenant?
Inclua tenant_id nas chaves do filho e do pai: FOREIGN KEY (tenant_id, document_id) REFERENCES documents (tenant_id, document_id).
Posso confiar em associações ORM sem FK?
Não - ORM não pode proteger contra SQL fora da aplicação. FK de banco de dados é o contrato de registro.
Como verificar FKs ausentes em CI?
Analise migrações para novas colunas *_id; exija REFERENCES correspondente ou um comentário de isenção explícito no template de PR.
Relacionados
- Modelagem de Entidade-Relacionamento - design de relacionamento
- Noções Básicas de Cenários de Defeito - detecção de órfãos
- Chaves Primárias e Estrangeiras - mecânica de FK
- Melhores Práticas para Cenários de Defeito - regras de lint
Versões da Stack: Esta página foi escrita para PostgreSQL 18.4 (stable 18, maintenance 17), pgvector 0.8+, PgBouncer 1.x, Patroni 3.x, e PostGIS 3.5+.