Restrições e Chaves
Restrições e Chaves
🌍 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.
🎯 Conceitos Fundamentais
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)
);
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;
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 |
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
);
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'))
);
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
);
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 |
📝 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
📌 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);
🎬 Cenários Práticos
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)
);
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
P: Uma tabela pode ter múltiplas restrições UNIQUE? R: Sim. Uma tabela pode ter apenas uma
PRIMARY KEY, mas pode ter múltiplas restriçõesUNIQUE. Por exemplo, tanto o e-mail quanto o telefone podem ter cada um uma restriçãoUNIQUE.
P: Chaves estrangeiras reduzem o desempenho? R: Chaves estrangeiras requerem verificações adicionais durante gravações, o que impacta ligeiramente o desempenho de escrita. Mas para a maioria das aplicações, a integridade dos dados é mais importante que a pequena diferença de desempenho. Para cenários de alta escrita, considere validação na camada da aplicação.
P: A restrição CHECK suporta subconsultas? R: As restrições
CHECKda 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.
P: Qual é a relação entre DEFAULT e NOT NULL? R: Se
NOT NULLestiver definido semDEFAULT, você deve fornecer explicitamente um valor ao inserir. SeDEFAULTestiver 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?
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.



