kb.erickguedes.com
SQL: Domínio de Bancos Relacionais

DDL — Modelagem de Dados

Aula 5 de 8

CREATE TABLE

CREATE TABLE clientes (
    id SERIAL PRIMARY KEY,
    nome VARCHAR(100) NOT NULL,
    email VARCHAR(200) UNIQUE NOT NULL,
    cpf CHAR(11) UNIQUE,
    data_nascimento DATE,
    telefone VARCHAR(20),
    endereco TEXT,
    ativo BOOLEAN DEFAULT true,
    data_cadastro TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    data_atualizacao TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Constraints

CREATE TABLE pedidos (
    id SERIAL PRIMARY KEY,
    cliente_id INTEGER NOT NULL,
    codigo VARCHAR(20) NOT NULL,
    status VARCHAR(20) DEFAULT 'pendente',
    total DECIMAL(10,2) NOT NULL CHECK (total >= 0),
    data_pedido TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    -- Foreign Key
    CONSTRAINT fk_cliente FOREIGN KEY (cliente_id) 
        REFERENCES clientes(id) 
        ON DELETE RESTRICT 
        ON UPDATE CASCADE,
    
    -- Unique composta
    CONSTRAINT uk_cliente_codigo UNIQUE (cliente_id, codigo),
    
    -- Check
    CONSTRAINT chk_status CHECK (status IN ('pendente', 'pago', 'cancelado'))
);

-- ON DELETE options:
-- RESTRICT  → impede deleção do pai se há filhos
-- CASCADE   → deleta filhos automaticamente
-- SET NULL  → seta FK como NULL
-- NO ACTION → similar RESTRICT (padrão)

ALTER TABLE

-- Adicionar coluna
ALTER TABLE clientes ADD COLUMN whatsapp VARCHAR(20);

-- Modificar tipo
ALTER TABLE clientes ALTER COLUMN telefone TYPE VARCHAR(30);

-- Adicionar constraint
ALTER TABLE pedidos ADD CONSTRAINT ck_valor_minimo CHECK (total >= 0);

-- Remover coluna
ALTER TABLE clientes DROP COLUMN endereco;

-- Renomear
ALTER TABLE clientes RENAME COLUMN whatsapp TO celular;
ALTER TABLE clientes RENAME TO clientes_ativos;

-- Default value
ALTER TABLE pedidos ALTER COLUMN status SET DEFAULT 'pendente';

-- NOT NULL
ALTER TABLE clientes ALTER COLUMN telefone SET NOT NULL;

INDEX — Performance

-- B-tree (padrão) — igualdade, range, ordenação
CREATE INDEX idx_pedidos_data ON pedidos (data_pedido);

-- Índice composto (ordem importa!)
CREATE INDEX idx_cliente_data ON pedidos (cliente_id, data_pedido DESC);
-- WHERE cliente_id = 1 ORDER BY data_pedido DESC usa este índice

-- Único
CREATE UNIQUE INDEX idx_cliente_email ON clientes (email);

-- Parcial
CREATE INDEX idx_pedidos_pendentes ON pedidos (data_pedido) 
WHERE status = 'pendente';

-- Hash (igualdade apenas, PostgreSQL)
CREATE INDEX idx_cliente_hash ON clientes USING HASH (id);

-- GIN (arrays, JSONB, full-text)
CREATE INDEX idx_produtos_tags ON produtos USING GIN (tags);
CREATE INDEX idx_produtos_descricao ON produtos USING GIN (to_tsvector('portuguese', descricao));

-- BRIN (dados ordenados, grandes volumes)
CREATE INDEX idx_vendas_data_brin ON vendas USING BRIN (data_pedido);

Normalização

Formas Normais:

1NF: Cada coluna atômica, sem grupos repetidos
    ❌ telefones VARCHAR(200) → "119999,118888"
    ✅ tabela separada telefones

2NF: 1NF + colunas não-chave dependem da chave completa
    ❌ pedido_id, produto_id, produto_nome (produto_nome depende só de produto_id)
    ✅ tabela separada produtos

3NF: 2NF + colunas não dependem de outras colunas não-chave
    ❌ cliente_id, cidade, uf (uf depende de cidade)
    ✅ tabela separada cidades

BCNF: 3NF + toda dependência é chave candidata

Lab: E-commerce Schema

CREATE TABLE categorias (
    id SERIAL PRIMARY KEY,
    nome VARCHAR(50) NOT NULL,
    categoria_pai_id INTEGER REFERENCES categorias(id),
    ativo BOOLEAN DEFAULT true
);

CREATE TABLE produtos (
    id SERIAL PRIMARY KEY,
    nome VARCHAR(200) NOT NULL,
    descricao TEXT,
    preco_custo DECIMAL(10,2) NOT NULL,
    preco_venda DECIMAL(10,2) NOT NULL CHECK (preco_venda >= preco_custo),
    categoria_id INTEGER NOT NULL REFERENCES categorias(id),
    estoque INTEGER DEFAULT 0 CHECK (estoque >= 0),
    ativo BOOLEAN DEFAULT true,
    criado_em TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_produtos_categoria ON produtos (categoria_id) WHERE ativo = true;
CREATE INDEX idx_produtos_busca ON produtos USING GIN (to_tsvector('portuguese', nome || ' ' || COALESCE(descricao, '')));

Normalização evita redundância. Índices aceleram consultas (mas custam escrita). Escolha o tipo de índice certo: B-tree (padrão), GIN (arrays/texto), BRIN (séries temporais).