Restrições e Chaves

Restrições e Chaves

🌍 Analogia do Mundo Real

Pense em um banco de dados como um escritório rigoroso:

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.

SQL
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):

SQL
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.

SQL
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
SQL
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).

SQL
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.

SQL
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.

SQL
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.

SQL
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

SQL
-- 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;
💡 Dica:

  • Nomes de restrições devem ser únicos dentro do banco de dados; formato recomendado: tabela_coluna_tipo_restrição
  • NOT NULL só pode ser adicionado usando ALTER TABLE ... MODIFY, não ADD 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

SQL
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
▶ Experimente

Exemplo: Adicionar e Remover Restrições em uma Tabela Existente

SQL
-- 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);
▶ Experimente

🎬 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.

SQL
-- 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.

SQL
-- 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ções UNIQUE. Por exemplo, tanto o e-mail quanto o telefone podem ter cada um uma restrição UNIQUE.

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 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.

P: Qual é a relação entre DEFAULT e NOT NULL? R: Se 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

📝 Exercícios

  1. Crie uma tabela customers com: chave primária, nome não nulo, e-mail único, telefone opcional, data de registro (padrão para hoje) e idade (18-120).
  2. Crie uma tabela orders com uma chave estrangeira referenciando customers, valor do pedido deve ser maior que 0, e status pode ser apenas pending, paid, shipped ou completed.
  3. Adicione uma restrição CHECK à tabela existente employees para garantir que o salário não fique abaixo do salário mínimo de 2000.
  4. Pense sobre: Se você excluir um departamento da tabela departments enquanto funcionários na tabela employees ainda pertencem a esse departamento, o que acontece com diferentes estratégias ON 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.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%