Introdução
Validar dados é uma responsabilidade que pode ficar na aplicação, no banco de dados, ou em ambos. Deixar tudo na aplicação é arriscado: diferentes sistemas podem acessar o mesmo banco, migrações podem rodar diretamente, um bug pode deixar passar um valor inválido. Constraints são regras definidas no próprio banco de dados — uma camada de proteção que age independente de quem está escrevendo os dados.
PRIMARY KEY
A chave primária identifica cada linha de forma única. Ela combina duas restrições implicitamente: NOT NULL e UNIQUE. Nenhuma linha pode ter o mesmo valor de chave primária, e nenhuma pode tê-la nula.
CREATE TABLE clientes (
id INT PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
Quando a chave primária envolve mais de uma coluna, ela é declarada separadamente:
CREATE TABLE matriculas (
aluno_id INT,
curso_id INT,
PRIMARY KEY (aluno_id, curso_id)
);
Na maioria dos bancos, é comum usar uma chave primária auto-incremental para não precisar gerenciar os IDs manualmente:
-- PostgreSQL
id SERIAL PRIMARY KEY
-- MySQL
id INT AUTO_INCREMENTPRIMARY KEY
-- SQL padrão (suportado por ambos)
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
FOREIGN KEY
A chave estrangeira garante integridade referencial: um valor só pode existir numa coluna se ele existir como chave primária na tabela referenciada. É o que torna os relacionamentos entre tabelas confiáveis.
CREATE TABLE pedidos (
id INT PRIMARY KEY,
cliente_idINT REFERENCES clientes(id)
);
Tentar inserir um pedido com cliente_id = 99 quando não existe cliente com esse id resulta em erro imediato. O banco rejeita a operação antes mesmo de ela chegar ao disco.
O comportamento quando o registro referenciado é deletado pode ser configurado:
CREATE TABLE pedidos (
id INT PRIMARY KEY,
cliente_id INT REFERENCES clientes(id)
ON DELETE CASCADE -- deleta os pedidos junto com o cliente
ON UPDATE CASCADE -- atualiza o cliente_id se o id do cliente mudar
);
As opções disponíveis são:
| Opção | Comportamento |
|---|---|
RESTRICT |
Rejeita a operação se houver dependentes (padrão) |
CASCADE |
Propaga a operação para os dependentes |
SET NULL |
Define a FK como nula nos dependentes |
SET DEFAULT |
Define a FK com o valor padrão da coluna |
UNIQUE
Garante que todos os valores de uma coluna — ou combinação de colunas — sejam distintos. Diferente da chave primária, uma coluna UNIQUE pode conter NULL (e em geral múltiplos NULL são permitidos, pois NULL não é igual a nada, nem a si mesmo).
CREATE TABLE usuarios (
id INT PRIMARY KEY,
email VARCHAR(150) UNIQUE,
username VARCHAR(50) UNIQUE
);
UNIQUE composto funciona da mesma forma que PRIMARY KEY composto — a restrição se aplica à combinação, não às colunas individualmente:
-- O mesmo aluno pode ter notas em vários cursos,
-- mas não duas notas no mesmo curso
CREATE TABLE notas (
aluno_id INT,
curso_id INT,
notaDECIMAL(4,2),
UNIQUE (aluno_id, curso_id)
);
NOT NULL
Impede que uma coluna aceite ausência de valor. Simples e direto — mas uma das constraints mais importantes na prática, porque NULL se propaga de formas inesperadas em cálculos e comparações.
CREATE TABLE produtos (
id INT PRIMARY KEY,
nomeVARCHAR(100) NOT NULL,
precoDECIMAL(10,2) NOT NULL,
descricao TEXT -- aceita NULL: campo opcional
);
Uma coluna sem NOT NULL aceita NULL por padrão. Definir quais campos são obrigatórios e quais são opcionais é uma decisão de modelagem — e NOT NULL é a forma de documentar e enforçar isso no próprio esquema.
Vale lembrar: AVG, SUM e outras funções de agregação ignoram NULL automaticamente. E comparações com NULL usando = sempre retornam falso — NULL = NULL é falso em SQL; a verificação correta é IS NULL.
CHECK
Permite definir uma condição arbitrária que os valores de uma coluna precisam satisfazer. É a constraint mais flexível — qualquer expressão booleana válida pode ser usada.
CREATE TABLE produtos (
id INT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
preco DECIMAL(10,2) NOT NULL CHECK (preco>= 0),
estoque INT NOT NULL CHECK (estoque>= 0),
avaliacao DECIMAL(2,1)CHECK (avaliacaoBETWEEN 0 AND 5)
);
Tentar inserir um produto com preco = -50 vai gerar um erro de violação de constraint antes de qualquer dado ser gravado.
CHECK também pode referenciar múltiplas colunas da mesma linha:
CREATE TABLE descontos (
id INT PRIMARY KEY,
preco_original DECIMAL(10,2) NOT NULL,
preco_final DECIMAL(10,2) NOT NULL,
CHECK (preco_final<= preco_original)
);
Isso garante que nenhum desconto resulte em preço final maior que o original — uma regra de negócio simples que, sem CHECK, dependeria inteiramente da aplicação para ser respeitada.
Adicionando Constraints depois
Constraints podem ser adicionadas a tabelas existentes com ALTER TABLE:
ALTER TABLE produtos
ADD CONSTRAINT chk_preco CHECK (preco>= 0);
ALTER TABLE pedidos
ADD CONSTRAINT fk_cliente
FOREIGN KEY (cliente_id) REFERENCES clientes(id);
E removidas quando necessário:
ALTER TABLE produtos DROP CONSTRAINT chk_preco;
Por que usar Constraints
A alternativa às constraints é validar tudo na aplicação. O problema é que a aplicação não é a única forma de os dados chegarem ao banco — migrações, scripts de manutenção, ferramentas de BI, outros serviços. Constraints garantem que independente da origem, os dados respeitem as regras. É a diferença entre confiar que ninguém vai errar e tornar o erro impossível.
Top comments (0)