Definição
O que são Constraints?
Constraints são regras aplicadas a colunas ou tabelas para garantir a integridade e validade dos dados. O Oracle automaticamente valida e aplica essas regras em todas as operações DML.
Inline
Definida diretamente na coluna durante CREATE TABLE.
Out-of-line
Definida separadamente, após as colunas ou via ALTER TABLE.
Tipos de Constraints
PRIMARY KEY (PK)
Identifica unicamente cada registro. Não permite NULL nem duplicatas.
FOREIGN KEY (FK)
Estabelece relacionamentos entre tabelas, referenciando uma PK.
UNIQUE (UK)
Garante valores únicos na coluna. Permite NULL (apenas um por coluna).
NOT NULL (NN)
Obriga que a coluna sempre tenha um valor.
CHECK (CK)
Valida valores baseado em condições customizadas.
DEFAULT
Define valor padrão quando nenhum valor é fornecido.
Aplicação Prática
PRIMARY KEY - Inline e Out-of-line
-- PRIMARY KEY INLINE (simples)
CREATE TABLE departamentos (
id_departamento NUMBER(10) PRIMARY KEY,
nome_departamento VARCHAR2(100) NOT NULL
);
-- PRIMARY KEY OUT-OF-LINE (nomeada)
CREATE TABLE departamentos (
id_departamento NUMBER(10),
nome_departamento VARCHAR2(100) NOT NULL,
CONSTRAINT pk_departamentos PRIMARY KEY (id_departamento)
);
-- PRIMARY KEY COMPOSTA (múltiplas colunas)
CREATE TABLE alocacoes (
funcionario_id NUMBER(10),
projeto_id NUMBER(10),
horas NUMBER(6,2),
data_alocacao DATE DEFAULT SYSDATE,
CONSTRAINT pk_alocacoes PRIMARY KEY (funcionario_id, projeto_id)
);
FOREIGN KEY - ON DELETE CASCADE e SET NULL
-- FOREIGN KEY INLINE (comportamento padrão: restritivo)
CREATE TABLE funcionarios (
id_funcionario NUMBER(10) PRIMARY KEY,
nome VARCHAR2(100) NOT NULL,
departamento_id NUMBER(10) REFERENCES departamentos(id_departamento)
);
-- FOREIGN KEY OUT-OF-LINE com ON DELETE CASCADE
-- Ao deletar departamento, deleta funcionários automaticamente
CREATE TABLE funcionarios (
id_funcionario NUMBER(10),
nome VARCHAR2(100) NOT NULL,
departamento_id NUMBER(10),
CONSTRAINT pk_funcionarios PRIMARY KEY (id_funcionario),
CONSTRAINT fk_func_depto FOREIGN KEY (departamento_id)
REFERENCES departamentos(id_departamento)
ON DELETE CASCADE
);
-- FOREIGN KEY com ON DELETE SET NULL
-- Ao deletar departamento, define departamento_id como NULL
CREATE TABLE funcionarios (
id_funcionario NUMBER(10),
nome VARCHAR2(100) NOT NULL,
departamento_id NUMBER(10),
CONSTRAINT pk_funcionarios PRIMARY KEY (id_funcionario),
CONSTRAINT fk_func_depto FOREIGN KEY (departamento_id)
REFERENCES departamentos(id_departamento)
ON DELETE SET NULL
);
-- FOREIGN KEY composta
CREATE TABLE alocacoes (
funcionario_id NUMBER(10),
projeto_id NUMBER(10),
horas NUMBER(6,2),
CONSTRAINT pk_alocacoes PRIMARY KEY (funcionario_id, projeto_id),
CONSTRAINT fk_aloc_func FOREIGN KEY (funcionario_id)
REFERENCES funcionarios(id_funcionario) ON DELETE CASCADE,
CONSTRAINT fk_aloc_proj FOREIGN KEY (projeto_id)
REFERENCES projetos(id_projeto) ON DELETE CASCADE
);
UNIQUE e NOT NULL
-- UNIQUE INLINE
CREATE TABLE funcionarios (
id_funcionario NUMBER(10) PRIMARY KEY,
nome VARCHAR2(100) NOT NULL,
email VARCHAR2(100) UNIQUE,
cpf VARCHAR2(14) NOT NULL UNIQUE
);
-- UNIQUE OUT-OF-LINE (nomeada)
CREATE TABLE funcionarios (
id_funcionario NUMBER(10),
nome VARCHAR2(100) NOT NULL,
email VARCHAR2(100),
cpf VARCHAR2(14) NOT NULL,
CONSTRAINT pk_funcionarios PRIMARY KEY (id_funcionario),
CONSTRAINT uk_func_email UNIQUE (email),
CONSTRAINT uk_func_cpf UNIQUE (cpf)
);
-- UNIQUE COMPOSTA (combinação de colunas)
CREATE TABLE contratos (
id_contrato NUMBER(10) PRIMARY KEY,
fornecedor_id NUMBER(10) NOT NULL,
numero_nf VARCHAR2(20) NOT NULL,
serie_nf VARCHAR2(5) NOT NULL,
valor NUMBER(15,2),
CONSTRAINT uk_nf_fornecedor UNIQUE (fornecedor_id, numero_nf, serie_nf)
);
-- NOT NULL sempre é inline
CREATE TABLE produtos (
id_produto NUMBER(10) PRIMARY KEY,
nome VARCHAR2(200) NOT NULL,
descricao VARCHAR2(4000), -- Opcional (permite NULL)
preco NUMBER(10,2) NOT NULL,
estoque_minimo NUMBER(10) DEFAULT 0 NOT NULL
);
CHECK - Validações Customizadas
-- CHECK INLINE
CREATE TABLE funcionarios (
id_funcionario NUMBER(10) PRIMARY KEY,
nome VARCHAR2(100) NOT NULL,
salario NUMBER(10,2) CHECK (salario > 0),
idade NUMBER(3) CHECK (idade >= 18 AND idade <= 70)
);
-- CHECK OUT-OF-LINE (nomeada e mais complexa)
CREATE TABLE funcionarios (
id_funcionario NUMBER(10),
nome VARCHAR2(100) NOT NULL,
salario NUMBER(10,2),
comissao NUMBER(10,2),
tipo_contrato VARCHAR2(20),
data_admissao DATE NOT NULL,
data_demissao DATE,
CONSTRAINT pk_funcionarios PRIMARY KEY (id_funcionario),
CONSTRAINT ck_func_salario CHECK (salario > 0),
CONSTRAINT ck_func_comissao CHECK (comissao >= 0 AND comissao < salario),
CONSTRAINT ck_func_tipo CHECK (tipo_contrato IN ('CLT', 'PJ', 'ESTAGIO', 'TEMPORARIO')),
CONSTRAINT ck_func_datas CHECK (data_demissao IS NULL OR data_demissao > data_admissao)
);
-- CHECK com múltiplas condições
CREATE TABLE pedidos (
id_pedido NUMBER(10) PRIMARY KEY,
valor_produtos NUMBER(15,2) NOT NULL,
valor_desconto NUMBER(15,2) DEFAULT 0,
valor_frete NUMBER(15,2) DEFAULT 0,
valor_total NUMBER(15,2) NOT NULL,
status VARCHAR2(20) NOT NULL,
CONSTRAINT ck_pedido_valores CHECK (
valor_produtos > 0 AND
valor_desconto >= 0 AND
valor_desconto <= valor_produtos AND
valor_frete >= 0 AND
valor_total = valor_produtos - valor_desconto + valor_frete
),
CONSTRAINT ck_pedido_status CHECK (
status IN ('PENDENTE', 'PAGO', 'ENVIADO', 'ENTREGUE', 'CANCELADO')
)
);
DEFAULT - Valores Padrão
CREATE TABLE auditoria_login (
id_log NUMBER(10) PRIMARY KEY,
usuario_id NUMBER(10) NOT NULL,
data_login DATE DEFAULT SYSDATE NOT NULL,
ip_origem VARCHAR2(45),
sucesso CHAR(1) DEFAULT 'S' CHECK (sucesso IN ('S', 'N')),
tentativas NUMBER(2) DEFAULT 1,
sessao_id VARCHAR2(100) DEFAULT SYS_GUID()
);
-- DEFAULT com funções Oracle
CREATE TABLE documentos (
id_documento NUMBER(10) PRIMARY KEY,
titulo VARCHAR2(200) NOT NULL,
conteudo CLOB,
data_criacao TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
data_modificacao TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
versao NUMBER(5) DEFAULT 1 NOT NULL,
ativo CHAR(1) DEFAULT 'S' CHECK (ativo IN ('S', 'N'))
);
Constraints Nomeadas vs Não Nomeadas
Importante!
Sempre nomeie suas constraints usando CONSTRAINT nome. Constraints não nomeadas recebem nomes automáticos (SYS_C0012345) difíceis de identificar.
-- ❌ RUIM: Constraint não nomeada
CREATE TABLE clientes (
id_cliente NUMBER(10) PRIMARY KEY,
email VARCHAR2(100) UNIQUE,
cpf VARCHAR2(14) CHECK (LENGTH(cpf) = 14)
);
-- Constraints recebem nomes como: SYS_C0012345, SYS_C0012346, SYS_C0012347
-- ✅ BOM: Constraints nomeadas
CREATE TABLE clientes (
id_cliente NUMBER(10),
email VARCHAR2(100),
cpf VARCHAR2(14),
CONSTRAINT pk_clientes PRIMARY KEY (id_cliente),
CONSTRAINT uk_clientes_email UNIQUE (email),
CONSTRAINT ck_clientes_cpf CHECK (LENGTH(cpf) = 14)
);
-- Fácil identificar e gerenciar!
ALTER TABLE - Adicionar e Remover Constraints
-- Adicionar PRIMARY KEY
ALTER TABLE funcionarios
ADD CONSTRAINT pk_funcionarios PRIMARY KEY (id_funcionario);
-- Adicionar FOREIGN KEY
ALTER TABLE funcionarios
ADD CONSTRAINT fk_func_depto FOREIGN KEY (departamento_id)
REFERENCES departamentos(id_departamento) ON DELETE SET NULL;
-- Adicionar UNIQUE
ALTER TABLE funcionarios
ADD CONSTRAINT uk_func_email UNIQUE (email);
-- Adicionar CHECK
ALTER TABLE funcionarios
ADD CONSTRAINT ck_func_salario CHECK (salario > 0);
-- Modificar coluna para NOT NULL
ALTER TABLE funcionarios
MODIFY nome VARCHAR2(100) NOT NULL;
-- Remover constraint
ALTER TABLE funcionarios
DROP CONSTRAINT ck_func_salario;
-- Remover PRIMARY KEY (e suas FKs dependentes)
ALTER TABLE departamentos
DROP PRIMARY KEY CASCADE;
-- Desabilitar constraint (temporariamente)
ALTER TABLE funcionarios
DISABLE CONSTRAINT fk_func_depto;
-- Reabilitar constraint
ALTER TABLE funcionarios
ENABLE CONSTRAINT fk_func_depto;
-- Reabilitar validando dados existentes
ALTER TABLE funcionarios
ENABLE VALIDATE CONSTRAINT fk_func_depto;
Consultando Constraints
-- Listar todas as constraints de uma tabela
SELECT
constraint_name,
constraint_type,
status,
search_condition
FROM user_constraints
WHERE table_name = 'FUNCIONARIOS';
-- Tipos de constraints:
-- P = PRIMARY KEY
-- R = FOREIGN KEY (Referential)
-- U = UNIQUE
-- C = CHECK ou NOT NULL
-- Ver detalhes de colunas nas constraints
SELECT
c.constraint_name,
c.constraint_type,
cc.column_name,
cc.position
FROM user_constraints c
JOIN user_cons_columns cc ON c.constraint_name = cc.constraint_name
WHERE c.table_name = 'FUNCIONARIOS'
ORDER BY c.constraint_name, cc.position;
-- Ver FOREIGN KEYs e suas referências
SELECT
fk.constraint_name AS fk_name,
fk.table_name AS tabela_filho,
fk_col.column_name AS coluna_filho,
pk.constraint_name AS pk_name,
pk.table_name AS tabela_pai,
pk_col.column_name AS coluna_pai,
fk.delete_rule
FROM user_constraints fk
JOIN user_cons_columns fk_col ON fk.constraint_name = fk_col.constraint_name
JOIN user_constraints pk ON fk.r_constraint_name = pk.constraint_name
JOIN user_cons_columns pk_col ON pk.constraint_name = pk_col.constraint_name
WHERE fk.constraint_type = 'R'
AND fk.table_name = 'FUNCIONARIOS';
-- Ver constraints CHECK com suas condições
SELECT
constraint_name,
search_condition
FROM user_constraints
WHERE table_name = 'FUNCIONARIOS'
AND constraint_type = 'C'
AND search_condition IS NOT NULL;
Resultado Esperado
O que você deve dominar após este tópico:
Todas as constraints
Dominar PK, FK, UNIQUE, NOT NULL, CHECK, DEFAULT
Inline vs Out-of-line
Escolher a sintaxe adequada para cada caso
Foreign keys cascata
Usar ON DELETE CASCADE e SET NULL
Nomear constraints
Criar nomes significativos e padronizados
CHECK constraints
Criar validações customizadas complexas
ALTER TABLE
Adicionar, remover e modificar constraints
Consultar metadata
Usar USER_CONSTRAINTS e USER_CONS_COLUMNS
Integridade de dados
Garantir qualidade e consistência dos dados
Exercícios Práticos
Exercício 1: Sistema de Pedidos
Crie as tabelas CLIENTES, PEDIDOS e ITENS_PEDIDO com todas as constraints apropriadas, incluindo PKs, FKs com CASCADE, CHECKs de validação e DEFAULTs.
Ver Solução
CREATE TABLE clientes (
id_cliente NUMBER(10),
nome VARCHAR2(200) NOT NULL,
cpf VARCHAR2(14) NOT NULL,
email VARCHAR2(100),
telefone VARCHAR2(20),
data_cadastro DATE DEFAULT SYSDATE NOT NULL,
status VARCHAR2(20) DEFAULT 'ATIVO' NOT NULL,
CONSTRAINT pk_clientes PRIMARY KEY (id_cliente),
CONSTRAINT uk_clientes_cpf UNIQUE (cpf),
CONSTRAINT uk_clientes_email UNIQUE (email),
CONSTRAINT ck_clientes_cpf CHECK (LENGTH(cpf) = 14),
CONSTRAINT ck_clientes_status CHECK (status IN ('ATIVO', 'INATIVO', 'BLOQUEADO'))
);
CREATE TABLE pedidos (
id_pedido NUMBER(10),
cliente_id NUMBER(10) NOT NULL,
data_pedido DATE DEFAULT SYSDATE NOT NULL,
valor_produtos NUMBER(15,2) NOT NULL,
valor_desconto NUMBER(15,2) DEFAULT 0,
valor_frete NUMBER(15,2) DEFAULT 0,
valor_total NUMBER(15,2) NOT NULL,
status VARCHAR2(20) DEFAULT 'PENDENTE' NOT NULL,
CONSTRAINT pk_pedidos PRIMARY KEY (id_pedido),
CONSTRAINT fk_pedidos_cliente FOREIGN KEY (cliente_id)
REFERENCES clientes(id_cliente) ON DELETE CASCADE,
CONSTRAINT ck_pedidos_valores CHECK (
valor_produtos > 0 AND
valor_desconto >= 0 AND
valor_desconto <= valor_produtos AND
valor_total = valor_produtos - valor_desconto + valor_frete
),
CONSTRAINT ck_pedidos_status CHECK (
status IN ('PENDENTE', 'PAGO', 'ENVIADO', 'ENTREGUE', 'CANCELADO')
)
);
CREATE TABLE itens_pedido (
pedido_id NUMBER(10),
produto_id NUMBER(10),
sequencia NUMBER(3),
quantidade NUMBER(10,2) NOT NULL,
preco_unitario NUMBER(15,2) NOT NULL,
desconto NUMBER(15,2) DEFAULT 0,
valor_total NUMBER(15,2) NOT NULL,
CONSTRAINT pk_itens_pedido PRIMARY KEY (pedido_id, produto_id, sequencia),
CONSTRAINT fk_itens_pedido FOREIGN KEY (pedido_id)
REFERENCES pedidos(id_pedido) ON DELETE CASCADE,
CONSTRAINT ck_itens_qtd CHECK (quantidade > 0),
CONSTRAINT ck_itens_preco CHECK (preco_unitario > 0),
CONSTRAINT ck_itens_desconto CHECK (desconto >= 0 AND desconto < preco_unitario),
CONSTRAINT ck_itens_total CHECK (
valor_total = (preco_unitario - desconto) * quantidade
)
);
Exercício 2: Adicionar Constraints
Uma tabela PRODUTOS foi criada sem constraints. Adicione todas as constraints necessárias usando ALTER TABLE para garantir integridade dos dados.
Ver Solução
-- Adicionar PRIMARY KEY
ALTER TABLE produtos
ADD CONSTRAINT pk_produtos PRIMARY KEY (id_produto);
-- Adicionar NOT NULL
ALTER TABLE produtos
MODIFY nome VARCHAR2(200) NOT NULL;
ALTER TABLE produtos
MODIFY preco NUMBER(10,2) NOT NULL;
-- Adicionar UNIQUE
ALTER TABLE produtos
ADD CONSTRAINT uk_produtos_codigo UNIQUE (codigo_barras);
-- Adicionar CHECK constraints
ALTER TABLE produtos
ADD CONSTRAINT ck_produtos_preco CHECK (preco > 0);
ALTER TABLE produtos
ADD CONSTRAINT ck_produtos_estoque CHECK (estoque_atual >= 0);
ALTER TABLE produtos
ADD CONSTRAINT ck_produtos_status CHECK (
status IN ('ATIVO', 'INATIVO', 'DESCONTINUADO')
);
-- Adicionar FOREIGN KEY
ALTER TABLE produtos
ADD CONSTRAINT fk_produtos_categoria FOREIGN KEY (categoria_id)
REFERENCES categorias(id_categoria) ON DELETE SET NULL;
-- Adicionar DEFAULT (requer MODIFY)
ALTER TABLE produtos
MODIFY status VARCHAR2(20) DEFAULT 'ATIVO';
ALTER TABLE produtos
MODIFY data_cadastro DATE DEFAULT SYSDATE;
Exercício 3: Auditoria de Constraints
Crie consultas SQL para listar todas as constraints de um schema, identificar FKs sem índices, e encontrar tabelas sem PRIMARY KEY.
Ver Solução
-- Listar todas as constraints por tipo
SELECT
table_name,
constraint_type,
COUNT(*) AS quantidade
FROM user_constraints
GROUP BY table_name, constraint_type
ORDER BY table_name, constraint_type;
-- Encontrar tabelas sem PRIMARY KEY
SELECT table_name
FROM user_tables
WHERE table_name NOT IN (
SELECT table_name
FROM user_constraints
WHERE constraint_type = 'P'
)
ORDER BY table_name;
-- Listar todas as FOREIGN KEYs com detalhes
SELECT
fk.table_name AS tabela_filho,
fk_col.column_name AS coluna_fk,
pk.table_name AS tabela_pai,
pk_col.column_name AS coluna_pk,
fk.constraint_name,
fk.delete_rule,
fk.status
FROM user_constraints fk
JOIN user_cons_columns fk_col ON fk.constraint_name = fk_col.constraint_name
JOIN user_constraints pk ON fk.r_constraint_name = pk.constraint_name
JOIN user_cons_columns pk_col ON pk.constraint_name = pk_col.constraint_name
WHERE fk.constraint_type = 'R'
ORDER BY fk.table_name, fk_col.position;
-- Identificar FKs sem índice (problema de performance)
SELECT
c.table_name,
c.constraint_name,
cc.column_name
FROM user_constraints c
JOIN user_cons_columns cc ON c.constraint_name = cc.constraint_name
WHERE c.constraint_type = 'R'
AND NOT EXISTS (
SELECT 1
FROM user_ind_columns ic
WHERE ic.table_name = c.table_name
AND ic.column_name = cc.column_name
AND ic.column_position = 1
)
ORDER BY c.table_name;
Parabéns! Você completou a Trilha 1
Continue sua jornada avançando para a Trilha 2, onde você aprenderá sobre Arquitetura e Administração do Oracle Database.