Presença e intervalo são regras diferentes
Um importador de movimentos sintéticos aceita um montante e um identificador externo. A regra exige um montante conhecido e positivo. CHECK(amount>0) exprime a comparação, mas um resultado desconhecido por causa de NULL não viola esse CHECK. Declara também NOT NULL quando o valor é obrigatório. Um DEFAULT ajuda a preencher entradas omitidas; não substitui a validação de valores explicitamente fornecidos. Antes de escolher SQL, escreve uma pequena tabela de aceitação com NULL, zero, negativo e positivo. Essa tabela permite discutir a regra com o negócio e evita transformar ausência de informação num valor aparentemente válido. O laboratório usa inteiros sintéticos para manter o foco no contrato.
A identidade pertence a um âmbito
O identificador externo R pode aparecer em dois tenants sem representar duplicação. UNIQUE(tenant_id,external_id), com ambas as colunas obrigatórias, protege a combinação relevante. Dois UNIQUE individuais imporiam regras diferentes e mais restritivas. Decide também o significado de NULL numa chave opcional: no PostgreSQL 18, UNIQUE permite por defeito vários NULL; NULLS NOT DISTINCT muda essa política. O teste portátil não executa essa sintaxe específica de PostgreSQL. Para uma referência entre tabelas, uma chave externa composta pode exigir que a tarefa e o projeto pertençam ao mesmo tenant. Isso preserva integridade dos dados; a aplicação continua responsável por verificar se o utilizador pode aceder a esse tenant.
Relações e eliminação precisam de política
Uma chave externa nullable não obriga cada tarefa a ter projeto. Se a relação é obrigatória, combina a referência com NOT NULL. Decide depois o que acontece ao eliminar o projeto. CASCADE pode ser correto quando os filhos fazem parte inseparável do parent, mas apaga dados dependentes. RESTRICT permite recusar a operação enquanto existirem referências, dando espaço para revisão humana. SET NULL representa outra regra e exige que ausência de relação seja permitida. Testa cada decisão com um projeto que tenha tarefas e outro sem dependências. No fixture SQLite, foreign_keys é ativado e confirmado antes das escritas; não se assume que a configuração de outra ligação seja herdada.
Conflito não significa repetição equivalente
Dois importadores podem verificar a ausência de uma chave antes de qualquer um inserir. A consulta prévia melhora mensagens, mas não substitui a restrição de unicidade na escrita. Define o resultado esperado quando surge o conflito. ON CONFLICT DO NOTHING evita inserir uma linha que colide com a chave escolhida; não compara automaticamente o significado de todos os campos. Se R já representa montante 25 e chega R com montante 30, devolver o resultado antigo como se o pedido fosse equivalente pode esconder um erro. Guarda informação suficiente para comparar conteúdo segundo o contrato. O exercício é uma decisão de desenho de idempotência, não uma promessa de transações distribuídas.
Laboratório: o âmbito da restrição
O código cria uma tabela local em que cada tenant tem identificadores externos únicos e montantes positivos obrigatórios. Insere R no tenant A e R no tenant B: ambos devem existir, porque a chave é composta. Acrescenta depois tentativas isoladas com NULL, zero e repetição de A/R. Prevê qual regra cada tentativa viola e confirma o resultado sem reutilizar dados de produção. Uma falha de instrução não define sozinha a aceitação do lote inteiro; liga este exercício à aula de transações e decide se reverterás todas as entradas. Em produção, uma migração precisa ainda de analisar dados existentes, bloqueios, tempo disponível e forma de recuperar de uma falha.
Resumo e diagnóstico de importações
Quando o suporte recebe uma rejeição de escrita, identifica primeiro a regra violada e o âmbito da chave. Distingue campo ausente, intervalo inválido, referência inexistente e identidade já usada. Não desatives a restrição apenas para concluir o lote: isso altera o contrato e pode transferir o problema para relatórios posteriores. Conserva uma amostra sintética mínima e testa a correção da entrada ou da regra aprovada. Relaciona esta aula com reconciliação e autorização: dados consistentes podem continuar a ser dados errados para o pedido ou para o utilizador. Os testes locais demonstram os exemplos escolhidos, mas não substituem a validação do esquema e da concorrência no motor real.
CREATE TABLE movements (
tenant_id TEXT NOT NULL,
external_id TEXT NOT NULL,
amount INTEGER NOT NULL CHECK (amount > 0),
UNIQUE (tenant_id, external_id)
);
INSERT INTO movements VALUES ('A','R',25), ('B','R',30);
SELECT tenant_id, external_id, amount
FROM movements
ORDER BY tenant_id, external_id;O mesmo identificador R é válido em tenants diferentes; repetir A/R exige tratar um conflito no âmbito de A.
Armadilhas comuns
Não equiparar CHECK a NOT NULL, unicidade da chave a equivalência do pedido, ou integridade entre tenants a autorização de acesso.
Tópicos relacionados: Contar e agrupar com cuidado · Índices e planos de execução · Reconciliação, métricas e diagnóstico de consultas
Define presença, valor, identidade e relação separadamente; decide como a aplicação comunica cada rejeição.
Referência: Constraints and identity · PostgreSQL 18 reference semantics; DR SQL 2026.4; synthetic plan metrics and portable SQLite 3.51.2 examples