DEV Community

Yuri Peixinho
Yuri Peixinho

Posted on

SQL: Data Constraints

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
);
Enter fullscreen mode Exit fullscreen mode

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)
);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
);
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

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)
);
Enter fullscreen mode Exit fullscreen mode

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
);
Enter fullscreen mode Exit fullscreen mode

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: AVGSUM 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)
);
Enter fullscreen mode Exit fullscreen mode

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)
);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

E removidas quando necessário:

ALTER TABLE produtos DROP CONSTRAINT chk_preco;
Enter fullscreen mode Exit fullscreen mode

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)