Introdução
Em 1970, um matemático da IBM chamado Edgar F. Codd publicou um paper chamado "A Relational Model of Data for Large Shared Data Banks". Antes disso, os bancos de dados eram majoritariamente hierárquicos ou baseados em arquivos simples (como o IMS da própria IBM, de 1966) — e a garantia de que os dados faziam sentido era inteiramente responsabilidade da aplicação. Se um programador esquecesse de checar se um cliente existia antes de gravar um pedido, ou se duas aplicações diferentes escreviam no mesmo arquivo com regras diferentes, os dados simplesmente ficavam inconsistentes. Não existia rede de segurança dentro do banco.
Quando Codd formalizou o modelo relacional, e depois, em 1985, publicou suas famosas "12 regras" para o que um banco verdadeiramente relacional deveria garantir, uma delas — a Regra 10, "Integrity Independence" — dizia algo radical para a época: as regras de integridade não podem depender da aplicação. Elas precisam viver dentro do banco, no catálogo, indissociáveis dos dados. É essa ideia, de quase 50 anos atrás, que sustenta tudo que vamos ver a seguir.
E não é só sobre corrupção de dados por acidente. É também sobre quem pode tocar no quê. Segundo o Cost of a Data Breach Report da IBM (2023), o custo médio global de uma violação de dados passou de US$ 4,45 milhões — e o Data Breach Investigations Report da Verizon, ano após ano, aponta credenciais comprometidas e uso indevido de privilégios como alguns dos vetores mais recorrentes de invasão. Integridade e segurança não são burocracia: são a diferença entre um banco confiável e um acidente esperando para acontecer.
Constraints
Constraints são regras que você declara junto com a estrutura da tabela, e que o próprio banco passa a fiscalizar automaticamente, sem depender de nenhuma aplicação lembrar de validar nada. Se uma regra é violada, o banco simplesmente recusa a operação, não importa de onde ela veio (seu backend, um script de outro time, uma query manual de madrugada).
NOT NULL, a garantia mais básica
CREATE TABLE clientes (
id INT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL
);
Impede que um campo essencial fique vazio. Parece trivial, mas é a constraint que mais previne bugs silenciosos em relatórios (COUNT, SUM e JOIN se comportam de forma traiçoeira quando encontram NULL inesperado, como vimos quando falamos de COALESCE e NULLIF).
UNIQUE, sem duplicatas
CREATE TABLE clientes (
id INT PRIMARY KEY,
cpf VARCHAR(11) UNIQUE,
email VARCHAR(100) UNIQUE
);
Garante que dois clientes nunca terão o mesmo CPF ou e-mail — o banco rejeita o segundo INSERT antes mesmo dele acontecer.
PRIMARY KEY, identidade única da linha
Combina NOT NULL + UNIQUE, e é o identificador que outras tabelas usam para apontar para essa linha:
CREATE TABLE produtos (
id INT PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
FOREIGN KEY, integridade referencial (a mais importante do artigo)
Esta é, historicamente, a constraint que Codd mais insistiu em formalizar. Ela garante que uma linha não pode referenciar algo que não existe:
CREATE TABLE pedidos (
id INT PRIMARY KEY,
cliente_id INT NOT NULL,
FOREIGN KEY (cliente_id) REFERENCES clientes(id)
);
Sem isso, seria possível criar um pedido apontando para cliente_id = 9999, quando o cliente 9999 nunca existiu (ou foi deletado) — um registro órfão, que quebra relatórios e JOINs silenciosamente.
O que fazer quando o "pai" é deletado? Isso é definido em ON DELETE:
FOREIGN KEY (cliente_id) REFERENCES clientes(id) ON DELETE CASCADE -- deleta os pedidos junto
FOREIGN KEY (cliente_id) REFERENCES clientes(id) ON DELETE SET NULL -- pedido fica "sem cliente"
FOREIGN KEY (cliente_id) REFERENCES clientes(id) ON DELETE RESTRICT-- impede deletar o cliente enquanto houver pedidos (padrão mais seguro)
CHECK, regras de negócio arbitrárias
CREATE TABLE produtos (
id INT PRIMARY KEY,
preco NUMERIC NOT NULL CHECK (preco > 0),
desconto NUMERIC CHECK (desconto BETWEEN 0 AND 100)
);
Diferente das constraints anteriores (que são estruturais), CHECK valida regras de negócio arbitrárias — preço não pode ser negativo, desconto não pode passar de 100%. É a forma mais direta de embutir uma regra de domínio na própria estrutura da tabela.
DEFAULT, preenchendo lacunas com sensatez
CREATE TABLE pedidos (
id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'pendente',
criado_em TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Não impede um valor inválido, mas garante que, na ausência de um valor explícito, o banco preenche algo sensato, evitando que NULLs se espalhem por falta de atenção da aplicação.
O fio condutor de tudo isso: a integridade sai da aplicação e vai para o banco. Exatamente a tese que Codd defendia em 1985, e que continua sendo a diferença entre um sistema que se autopolicia e um que depende da sorte de todo desenvolvedor lembrar de validar tudo, sempre, em todo lugar que escreve no banco.
Top comments (0)