sqlpostgresqlconstraintscheck

Restricoes CHECK no SQL: invariantes no nivel do banco

Use CHECK para fixar regras de negocio no esquema: valores positivos, status validos, intervalos de datas e validacoes multicoluna.

2 min de leituraReferencesql · postgresql · constraints · check · data-integrity

Uma restricao CHECK e uma condicao booleana que o banco avalia a cada insercao ou atualizacao de linha. Se a expressao retornar FALSE, a operacao falha. Assim voce fixa uma regra de negocio no esquema em vez de deixa-la no codigo da aplicacao, que qualquer outro cliente pode contornar.

Por que manter invariantes no banco

Sua aplicacao pode esquecer de validar. Em producao convivem varios servicos, e migracoes ou UPDATE manuais passam totalmente por fora do seu codigo. Um CHECK garante que uma linha invalida simplesmente nao seja gravada, venha de onde vier a consulta.

CREATE TABLE orders (
  id         bigint PRIMARY KEY,
  user_id    bigint NOT NULL,
  amount     numeric(12,2) NOT NULL CHECK (amount > 0),
  status     text NOT NULL CHECK (status IN ('new', 'paid', 'shipped', 'cancelled')),
  created_at timestamptz NOT NULL DEFAULT now()
);

Sao duas regras: o valor e estritamente positivo e o status precisa estar em um conjunto permitido. Inserir amount = 0 ou status = 'refunded' gera um erro.

Restricoes nomeadas

Um CHECK anonimo recebe um nome gerado automaticamente, como orders_amount_check. Prefira nomear de forma explicita: o nome aparece nas mensagens de erro e da um identificador limpo para o DROP CONSTRAINT.

ALTER TABLE products
  ADD CONSTRAINT price_positive CHECK (price > 0);

-- Later, to change the rule:
ALTER TABLE products DROP CONSTRAINT price_positive;
  • O nome precisa ser unico dentro da tabela.
  • Um nome descritivo (price_positive, salary_nonneg) e bem mais legivel nos logs do que um gerado automaticamente.

Verificacoes multicoluna e intervalos

Uma expressao CHECK pode referenciar varias colunas da mesma linha, ideal para intervalos e coerencia entre campos.

ALTER TABLE employees
  ADD CONSTRAINT salary_band CHECK (salary BETWEEN 30000 AND 500000),
  ADD CONSTRAINT not_self_manager CHECK (manager_id IS NULL OR manager_id <> id);

ALTER TABLE users
  ADD CONSTRAINT created_not_future CHECK (created_at <= now());

Limite importante: um CHECK so enxerga a linha atual. Ele nao pode ler outras linhas nem outras tabelas (para isso use chaves estrangeiras ou triggers). No PostgreSQL a expressao tambem deve ser deterministica; now() e tecnicamente aceito, mas amarrar uma verificacao ao "horario atual" e fragil, porque ela so e avaliada na hora da gravacao.

NULL e a logica de tres valores

A grande pegadinha. Se a expressao resultar em NULL (nem TRUE nem FALSE), o CHECK considera a linha valida. Ou seja, um CHECK so bloqueia valores inequivocamente falsos.

-- country may be NULL, and that PASSES the check:
ALTER TABLE users
  ADD CONSTRAINT country_iso CHECK (country IN ('US', 'GB', 'DE', 'BR'));

INSERT INTO users (id, email, country) VALUES (1, 'a@x.io', NULL); -- OK!

Para proibir NULL, adicione um NOT NULL separado ou inclua o teste na condicao:

ALTER TABLE users
  ADD CONSTRAINT country_required
  CHECK (country IS NOT NULL AND country IN ('US', 'GB', 'DE', 'BR'));

Adicionando em tabelas grandes: NOT VALID

ADD CONSTRAINT ... CHECK varre a tabela inteira enquanto mantem um bloqueio; com milhoes de linhas isso e uma parada longa. O PostgreSQL permite adicionar a restricao como NOT VALID: ela passa a valer de imediato para linhas novas e alteradas, mas as existentes nao sao verificadas. Depois voce as valida em um passo separado e mais leve.

ALTER TABLE orders
  ADD CONSTRAINT amount_positive CHECK (amount > 0) NOT VALID;

-- Validate existing rows without a long exclusive lock:
ALTER TABLE orders VALIDATE CONSTRAINT amount_positive;

Diferencas entre engines:

  • MySQL suporta CHECK a partir da 8.0.16; versoes anteriores aceitavam a sintaxe mas a ignoravam em silencio. Aqui nao existe NOT VALID.
  • ClickHouse tem CONSTRAINT ... CHECK, mas e avaliado na insercao e otimizado para analitica de outra forma; nao conte com ele como invariante estrito de OLTP.

Resumo: use CHECK para invariantes simples e locais a linha, nomeie sempre, lembre da semantica de NULL e recorra ao NOT VALID para subir com seguranca em producao.

Pratique com exercícios reais

Resolva exercícios no treinador de SQL com correção instantânea e dicas.

Abrir o treinador