SQL: Restrições e Chaves
Última atualização: 2026-10-09
1. 🌍 Analogia do Mundo Real
Pense em um banco de dados como um escritório rigoroso:
- PRIMARY KEY = O crachá de cada funcionário, nunca duplicado, nunca nulo
- FOREIGN KEY = Um funcionário deve pertencer a um departamento que realmente existe
- UNIQUE = O e-mail de cada pessoa deve ser único
- NOT NULL = O campo nome não pode ficar em branco
- CHECK = A idade deve estar entre 18 e 65
- DEFAULT = Quando nenhum status é preenchido, o padrão é "Ativo"
Restrições são os "guardiões de regras" de um banco de dados — verificam automaticamente os dados à medida que são gravados, impedindo que dados sujos entrem.
2. 🎯 Conceitos Fundamentais
(1) PRIMARY KEY
Identifica exclusivamente cada linha em uma tabela. Não permite duplicatas, não permite NULL. Uma tabela pode ter apenas uma chave primária.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL
);
Uma chave primária também pode consistir em múltiplas colunas (chave primária composta):
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT,
PRIMARY KEY (order_id, product_id)
);
(2) FOREIGN KEY
Garante que os valores em uma tabela devem existir em outra tabela, mantendo a integridade referencial.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
department_id INT,
FOREIGN KEY (department_id) REFERENCES departments(department_id)
);
Operações de cascata de chave estrangeira:
| Operação | Descrição |
|---|---|
ON DELETE CASCADE |
Quando a linha da tabela pai é excluída, as linhas relacionadas na tabela filha também são excluídas |
ON DELETE SET NULL |
Quando a linha da tabela pai é excluída, a chave estrangeira na tabela filha é definida como NULL |
ON DELETE RESTRICT |
A exclusão é proibida se existirem referências (padrão) |
ON UPDATE CASCADE |
Quando a chave primária da tabela pai é atualizada, a chave estrangeira da tabela filha é atualizada correspondentemente |
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL
ON UPDATE CASCADE;
(3) Restrição UNIQUE
Garante que todos os valores em uma coluna não sejam duplicados, mas permite NULL (na maioria dos bancos de dados, múltiplos NULLs são permitidos).
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
email VARCHAR(100) UNIQUE
);
Diferenças entre UNIQUE e PRIMARY KEY:
| Característica | PRIMARY KEY | UNIQUE |
|---|---|---|
| Valores NULL | ❌ Não permitido | ✅ Permitido |
| Quantidade | Apenas 1 por tabela | Pode ter múltiplos |
| Propósito | Identificador exclusivo de linha | Unicidade de valor da coluna |
(4) Restrição NOT NULL
Garante que uma coluna não pode armazenar valores NULL.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL
);
(5) Restrição CHECK
Garante que os valores em uma coluna satisfaçam uma condição especificada.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
salary DECIMAL(10,2) CHECK (salary > 0),
age INT CHECK (age >= 18 AND age <= 65),
gender CHAR(1) CHECK (gender IN ('M', 'F'))
);
(6) Valor DEFAULT
Ao inserir dados sem especificar um valor para a coluna, o valor padrão é usado automaticamente.
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'active',
hire_date DATE DEFAULT CURRENT_DATE,
salary DECIMAL(10,2) DEFAULT 5000.00
);
(7) Princípios de Design de Restrições
| Princípio | Descrição |
|---|---|
| Privilégio mínimo | Aplique apenas restrições necessárias; não restrinja demais |
| Validar próximo à fonte | Defina no nível de coluna quando possível, não no nível de tabela |
| Convenção de nomenclatura | Dê nomes significativos às restrições para facilitar a manutenção |
| Negócio primeiro | As restrições devem refletir regras de negócio, não limitações técnicas |
3. 📝 Sintaxe Básica
-- Definir restrições ao criar uma tabela
CREATE TABLE tabela (
coluna1 tipo_dado PRIMARY KEY,
coluna2 tipo_dado NOT NULL,
coluna3 tipo_dado UNIQUE,
coluna4 tipo_dado CHECK (condição),
coluna5 tipo_dado DEFAULT valor,
coluna6 tipo_dado,
FOREIGN KEY (coluna6) REFERENCES outra_tabela(coluna)
);
-- Adicionar restrição a uma tabela existente
ALTER TABLE tabela ADD CONSTRAINT nome_restrição
UNIQUE (nome_coluna);
ALTER TABLE tabela ADD CONSTRAINT nome_restrição
CHECK (condição);
-- Remover restrição
ALTER TABLE tabela DROP CONSTRAINT nome_restrição;
- Nomes de restrições devem ser únicos dentro do banco de dados; formato recomendado:
tabela_coluna_tipo_restrição NOT NULLsó pode ser adicionado usandoALTER TABLE ... MODIFY, nãoADD CONSTRAINT- Adicionar uma restrição a uma tabela com dados existentes pode falhar se os dados existentes não satisfizerem a condição
4. 📌 Exemplos
▶ Exemplo: Criar uma Tabela de Funcionários com Restrições
CREATE TABLE employees (
employee_id INT PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
hire_date DATE NOT NULL DEFAULT CURRENT_DATE,
salary DECIMAL(10,2) NOT NULL CHECK (salary > 0),
department_id INT,
CONSTRAINT fk_emp_dept FOREIGN KEY (department_id)
REFERENCES departments(department_id)
ON DELETE SET NULL
);
-- Inserir dados de teste
INSERT INTO employees (employee_id, first_name, last_name, email, hire_date, salary, department_id)
VALUES (1, 'John', 'Doe', 'johndoe@company.com', '2026-01-15', 8500.00, 1);
-- As instruções a seguir falharão:
-- INSERT ... salary = -100 -- ❌ Restrição CHECK
-- INSERT ... first_name NULL -- ❌ Restrição NOT NULL
-- INSERT ... email duplicado -- ❌ Restrição UNIQUE
▶ Exemplo: Adicionar e Remover Restrições em uma Tabela Existente
-- Adicionar restrição CHECK: salário não deve exceder 500000
ALTER TABLE employees ADD CONSTRAINT chk_salary_max
CHECK (salary <= 500000);
-- Adicionar restrição UNIQUE: número de telefone deve ser único
ALTER TABLE employees ADD CONSTRAINT uq_emp_phone
UNIQUE (phone);
-- Remover restrição
ALTER TABLE employees DROP CONSTRAINT chk_salary_max;
-- Adicionar restrição de chave estrangeira
ALTER TABLE employees ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id) REFERENCES departments(department_id);
5. 🎬 Cenários Práticos
(1) Cenário 1: Design de Restrições para Sistema de Pedidos de E-Commerce
Projetar uma tabela de pedidos para garantir a integridade dos dados.
-- Tabela de produtos
CREATE TABLE products (
product_id INT PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL CHECK (price > 0),
stock INT NOT NULL DEFAULT 0 CHECK (stock >= 0),
status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'discontinued'))
);
-- Tabela de pedidos
CREATE TABLE orders (
order_id INT PRIMARY KEY,
customer_id INT NOT NULL,
order_date DATE NOT NULL DEFAULT CURRENT_DATE,
total_amount DECIMAL(12,2) CHECK (total_amount > 0),
status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'shipped', 'cancelled')),
CONSTRAINT fk_order_customer FOREIGN KEY (customer_id)
REFERENCES customers(customer_id)
);
-- Tabela de itens do pedido (chave primária composta)
CREATE TABLE order_items (
order_id INT,
product_id INT,
quantity INT NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price > 0),
PRIMARY KEY (order_id, product_id),
FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE,
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
(2) Cenário 2: Migração de Restrições do Sistema de Gerenciamento de Funcionários
Adicionar restrições a uma tabela existente.
-- Tabela existente falta restrições; adicionar passo a passo
-- 1. Garantir que o formato do e-mail está correto
ALTER TABLE employees ADD CONSTRAINT chk_email_format
CHECK (email LIKE '%@%.%');
-- 2. Garantir que a data de contratação é razoável
ALTER TABLE employees ADD CONSTRAINT chk_hire_date
CHECK (hire_date >= '2000-01-01' AND hire_date <= CURRENT_DATE);
-- 3. Garantir relacionamento de chave estrangeira com departamento
ALTER TABLE employees ADD CONSTRAINT fk_emp_dept
FOREIGN KEY (department_id) REFERENCES departments(department_id)
ON DELETE SET NULL;
-- 4. Visualizar todas as restrições de uma tabela (SQL Server)
SELECT name, type_desc
FROM sys.objects
WHERE parent_object_id = OBJECT_ID('employees')
AND type IN ('C', 'F', 'UQ', 'PK');
❓ Perguntas Frequentes
PRIMARY KEY, mas pode ter múltiplas restrições UNIQUE. Por exemplo, tanto o e-mail quanto o telefone podem ter cada um uma restrição UNIQUE.CHECK da maioria dos bancos de dados não suportam subconsultas. Se você precisar de validação entre tabelas, use triggers ou implemente na camada da aplicação.NOT NULL estiver definido sem DEFAULT, você deve fornecer explicitamente um valor ao inserir. Se DEFAULT estiver definido, você pode omitir a coluna durante a inserção e o valor padrão será usado automaticamente. Eles são frequentemente usados juntos.📖 Resumo
| Restrição | Propósito | Permite NULL | Permite Múltiplas |
|---|---|---|---|
| PRIMARY KEY | Identificador exclusivo de linha | ❌ | 1 por tabela |
| FOREIGN KEY | Referencia a chave primária de outra tabela | ✅ | ✅ |
| UNIQUE | Unicidade de valor da coluna | ✅ | ✅ |
| NOT NULL | Coluna não pode ser nula | ❌ | ✅ |
| CHECK | Valor da coluna satisfaz condição | Depende da definição | ✅ |
| DEFAULT | Valor padrão quando não especificado | - | 1 por coluna |
- Restrições são a "primeira linha de defesa" do banco de dados, validando automaticamente os dados à medida que são gravados
- Prefira definir restrições no nível do banco de dados em vez de depender exclusivamente da camada da aplicação
- Nomeie suas restrições para facilitar manutenção e depuração
📝 Exercícios
- Crie uma tabela
customerscom: chave primária, nome não nulo, e-mail único, telefone opcional, data de registro (padrão para hoje) e idade (18-120). - Crie uma tabela
orderscom uma chave estrangeira referenciandocustomers, valor do pedido deve ser maior que 0, e status pode ser apenaspending,paid,shippedoucompleted. - Adicione uma restrição
CHECKà tabela existenteemployeespara garantir que o salário não fique abaixo do salário mínimo de 2000. - Pense sobre: Se você excluir um departamento da tabela
departmentsenquanto funcionários na tabelaemployeesainda pertencem a esse departamento, o que acontece com diferentes estratégiasON DELETE?
6. Próxima Lição
A seguir aplicaremos tudo através de Prática: Consulta Multitabelas Abrangente — combinando JOINs, subconsultas e operações com conjuntos para resolver cenários reais de negócio.