🔒 CAPÍTULO 12: RESTRIÇÕES DE INTEGRIDADE E CONSTRAINTS
As restrições (Constraints) são as muralhas do seu banco de dados. Elas garantem que os dados inseridos no banco sigam as regras de negócio rigorosas e mantenham a consistência da informação, impedindo que a aplicação salve um preço negativo ou um cliente sem CPF. 🛡️🧩
🎯 Objetivo Curricular
Dominar as restrições de integridade (NOT NULL, UNIQUE, DEFAULT, CHECK) e as Ações Referenciais em Chaves Estrangeiras (CASCADE, RESTRICT) para blindar o banco de dados contra lixo digital.
🏢 O Cenário Prático (Seu Desafio)
A equipe de auditoria da TecProExpress encontrou anomalias gravíssimas na base de testes: existiam produtos cadastrados com peso negativo, motoristas cadastrados com a mesma placa de CNH e registros vazios.
"Seu desafio é reescrever a DDL do sistema de transportes adicionando
Constraints(Restrições) diretas no motor SQL. Se um programador tentar inserir um pacote com peso de-5 kg, o SGBD deve cuspir um erro de volta na cara da aplicação antes mesmo do dado chegar no disco!"
🧠 Fundamentos: O Arsenal de Defesa (Constraints)
Você já usou a restrição máxima (PRIMARY KEY), mas o DDL nos fornece ferramentas mais granulares:
| Constraint | Função de Defesa | Exemplo Prático |
|---|---|---|
| NOT NULL | O campo é obrigatório. | Nenhum usuário sem Nome. |
| DEFAULT | Valor automático se ninguém enviar nada. | Cliente novo entra com status Ativo por padrão. |
| UNIQUE | Impede valor repetido (mas aceita NULL). | O E-mail da conta de acesso. |
| CHECK | Validações matemáticas e lógicas. | O frete deve ser sempre >= 0. |
📖 Exemplo Guiado: O DDL Blindado (Regras de Negócio)
Vamos criar a tabela de "Frete" da TecProExpress com uma armadura pesada de validações.
🛠️ Código do Exemplo
-- PASSO 1: DDL (Criando a Tabela Blindada)
CREATE TABLE frete_blindado (
id INT PRIMARY KEY,
codigo_rastreio CHAR(10) UNIQUE NOT NULL, -- Obrigatório e Exclusivo
peso_kg DECIMAL(5,2) CHECK (peso_kg > 0), -- A regra de ouro da auditoria
data_registro DATE DEFAULT CURRENT_DATE, -- Se omitido, pega o dia de hoje
status VARCHAR(20) DEFAULT 'PENDENTE'
);
-- PASSO 2: DML (Carga de Sucesso)
-- Omitimos a data e o status para ver o DEFAULT agir!
INSERT INTO frete_blindado (id, codigo_rastreio, peso_kg)
VALUES (1, 'AB12345678', 15.50);
-- PASSO 3: DML (A Carga que VAI FALHAR - Teste o CHECK)
-- Descomente e rode. O banco VAI recusar a inserção por causa do peso negativo.
-- INSERT INTO frete_blindado (id, codigo_rastreio, peso_kg) VALUES (2, 'XY99999999', -2.00);
🔍 Detalhamento do Código:
DEFAULT CURRENT_DATE: Uma função nativa. Se a aplicação de frontend esquecer de mandar a data, o próprio banco preenche.CHECK (peso_kg > 0): A parede intransponível. A aplicação Node.js ou Python vai receber um erro "Constraint Violation" caso tente registrar peso negativo. O banco de dados nunca confia no Frontend!
🔗 Ações Referenciais em Chaves Estrangeiras (FK)
A Chave Estrangeira não serve apenas para "vincular". Ela define o que acontece quando alguém apaga o dado PAI.
- RESTRICT (Padrão): O SGBD proíbe a deleção do Cliente se ele tiver Pacotes no sistema.
- CASCADE: Se você deletar o Cliente, o SGBD deleta todos os Pacotes dele junto. (Use com extrema sabedoria).
- SET NULL: Deleta o Cliente e deixa os Pacotes órfãos (com ID
NULL). Útil se você não quer apagar o histórico de transporte, mas o cliente cancelou a conta.
📊 Fluxo da Chave Estrangeira
flowchart TD
DEL["❌ Ação: DELETE FROM Cliente WHERE id=10"] --> SGBD{"⚙️ Motor de FK"}
SGBD -- "RESTRICT" --> B1["⛔ ERRO: Ação Bloqueada"]
SGBD -- "CASCADE" --> B2["🔥 Deleção em Massa (Cliente e Pacotes)"]
SGBD -- "SET NULL" --> B3["🧹 Apaga o Cliente. Atualiza Pacotes para NULL"]
🛠️ Prática Obrigatória: Nomeando as Muralhas
Cenário: Facilitando o debug do desenvolvedor.
Se você não der um nome para o seu CHECK, o banco cria um nome feio (ex: SYS_C0015). Veja como nomear sua restrição para o log de erro ficar legível.
- Crie a tabela de
funcionariocom um CHECK de salário nomeado comochk_salario_minimo.
🚀 Script de Seed (Gabarito de Nomenclatura)
-- DDL
CREATE TABLE funcionario (
id INT PRIMARY KEY,
nome VARCHAR(100),
salario DECIMAL(10,2),
-- O 'CONSTRAINT' antes da regra permite batizá-la!
CONSTRAINT chk_salario_minimo CHECK (salario >= 1412.00)
);
-- DML (Essa linha falha e o erro vai explicitar o nome 'chk_salario_minimo')
-- INSERT INTO funcionario VALUES (1, 'João', 1000.00);
📤 Instruções de Entrega (Microsoft Teams)
Após validar seus códigos práticos e de estudo:
Use o operador e para critérios rigorosos e ou para critérios flexíveis. Salve os arquivos com a extensão .sql (Ex: Atividade_XX_SeuNome.sql ou Atividade_XX_SeuNome.png ou Atividade_XX_SeuNome.drawio
💡 Checkpoint de Lógica
[!TIP] Dica do Especialista: Uma arquitetura de dados sênior deve sempre possuir uma "Defesa em Profundidade" (Defense in Depth). Não é porque a linguagem de programação no frontend tem um "IF" bloqueando pesos negativos que o banco de dados deve aceitar qualquer coisa. A Constraint é o goleiro do seu time: se a defesa do código falhar, o goleiro do banco espalma a bola! 🚀🛡️