Início / Trilha 1 / Conteúdo / Tópico 15
15

Constraints Completas

Todas as constraints e integridade de dados no Oracle

📖

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.

Anterior: Views e Synonyms Próxima Trilha: Arquitetura Oracle