Hoje vamos destrinchar como montar um procedimento real pra processar pedidos. Ele tem várias etapas: validação dos dados, atualização do status do pedido e também logging. Imagina um restaurante, onde o chef, o garçom e o caixa precisam agir juntos. No nosso procedimento, vamos implementar uma lógica parecida de interação entre as etapas.
Descrição da tarefa do procedimento
O procedimento pra processar pedidos deve fazer o seguinte:
- Checar se o produto necessário está disponível no estoque.
- Se tiver produto suficiente, dar baixa na quantidade no estoque.
- Atualizar o status do pedido pra "Processado".
- Registrar a informação da operação bem-sucedida no log.
- Se rolar qualquer erro, dar rollback em tudo.
Implementação do procedimento
Passo 1. Criando o schema e as tabelas pra trabalhar
Antes de escrever o procedimento, vamos criar as tabelas que ele vai usar.
Tabela orders — pedidos
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
customer_name TEXT NOT NULL,
product_id INT NOT NULL,
quantity INT NOT NULL CHECK (quantity > 0),
status TEXT DEFAULT 'Pending'
);
Essa tabela guarda os pedidos. Cada pedido tem um cliente, id do produto, quantidade e status (por padrão "Pendente").
Tabela inventory — estoque
CREATE TABLE inventory (
product_id SERIAL PRIMARY KEY,
product_name TEXT NOT NULL UNIQUE,
stock INT NOT NULL CHECK (stock >= 0)
);
Tabela com a lista de produtos no estoque. Cada produto tem o estoque atual (stock).
Tabela order_logs — log de operações
CREATE TABLE order_logs (
log_id SERIAL PRIMARY KEY,
order_id INT NOT NULL REFERENCES orders(order_id) ON DELETE CASCADE,
log_message TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
O log vai ser usado pra registrar informações sobre o status do processamento dos pedidos.
Passo 2. Estrutura do procedimento
Aqui está a estrutura do procedimento multi-etapas:
- Checar se o produto pedido existe no estoque e se tem quantidade suficiente.
- Se tiver produto suficiente, diminuir a quantidade na tabela
inventory. - Mudar o status do pedido pra "Processado".
- Registrar o sucesso na tabela
order_logs. - Tratar possíveis erros com rollback.
Passo 3. Escrevendo o procedimento
Bora escrever o procedimento process_order pra executar os passos acima.
CREATE OR REPLACE FUNCTION process_order(order_id INT)
RETURNS VOID AS $$
DECLARE
v_product_id INT;
v_quantity INT;
v_stock INT;
BEGIN
-- Passo 1: Pega info do pedido
SELECT product_id, quantity
INTO v_product_id, v_quantity
FROM orders
WHERE order_id = $1;
-- Checa se o pedido existe
IF NOT FOUND THEN
RAISE EXCEPTION 'Pedido com ID % não existe.', $1;
END IF;
-- Passo 2: Checa se tem produto no estoque
SELECT stock INTO v_stock
FROM inventory
WHERE product_id = v_product_id;
IF NOT FOUND THEN
RAISE EXCEPTION 'Produto com ID % não existe no estoque.', v_product_id;
END IF;
IF v_stock < v_quantity THEN
RAISE EXCEPTION 'Estoque insuficiente para o produto ID %. Solicitado: %, Disponível: %.',
v_product_id, v_quantity, v_stock;
END IF;
-- Passo 3: Diminui a quantidade do produto no estoque
UPDATE inventory
SET stock = stock - v_quantity
WHERE product_id = v_product_id;
-- Passo 4: Atualiza status do pedido pra 'Processed'
UPDATE orders
SET status = 'Processed'
WHERE order_id = $1;
-- Passo 5: Registra sucesso no log
INSERT INTO order_logs (order_id, log_message)
VALUES ($1, 'Pedido processado com sucesso.');
EXCEPTION
WHEN OTHERS THEN
-- Loga o erro se der ruim
INSERT INTO order_logs (order_id, log_message)
VALUES ($1, 'Erro ao processar pedido: ' || SQLERRM);
-- Dá rollback em tudo
RAISE;
END;
$$ LANGUAGE plpgsql;
Bora analisar esse procedimento.
Etapa de verificação:
a gente checa se o pedido existe na tabela
orders. Se não achar, lança uma exceção com mensagem detalhada. Do mesmo jeito, checa se o produto existe e se tem quantidade suficiente no estoque.Etapa do estoque:
se tiver produto suficiente, diminui a quantidade no estoque. Isso é feito com
UPDATE.Etapa de mudança de status do pedido:
muda o status pra "Processed" (Processado) pra indicar que o pedido foi finalizado de boa.
Etapa de logging:
depois de processar o pedido com sucesso, adiciona uma mensagem na tabela
order_logspra guardar a info da operação.Tratamento de exceções:
se der ruim em qualquer coisa, a gente pega o erro no bloco
EXCEPTION, registra no log uma mensagem detalhada do erro e faz rollback de tudo.
Exemplos de uso
Vamos criar uns dados de teste pra ver como o procedimento funciona.
-- Adiciona produto no estoque
INSERT INTO inventory (product_name, stock)
VALUES ('Laptop', 10), ('Monitor', 5);
-- Adiciona pedidos
INSERT INTO orders (customer_name, product_id, quantity)
VALUES
('Alice', 1, 2),
('Bob', 2, 1),
('Charlie', 1, 20); -- Esse pedido deve dar erro
Agora vamos testar o procedimento:
-- Processa pedido da Alice
SELECT process_order(1);
-- Processa pedido do Bob
SELECT process_order(2);
-- Tenta processar pedido do Charlie (erro)
SELECT process_order(3);
Resultados:
- Os pedidos da Alice e do Bob vão ser processados de boa, registrados no log e o estoque vai diminuir.
- O pedido do Charlie vai dar erro porque não tem produto suficiente no estoque, e o log vai registrar o erro.
Vamos conferir as tabelas depois de rodar os comandos:
SELECT * FROM inventory; -- Mudanças no estoque
SELECT * FROM orders; -- Mudanças nos status dos pedidos
SELECT * FROM order_logs; -- Registros no log
Erros comuns e dicas
Erro: esqueceu de checar
NOT FOUNDdepois doSELECT INTO.Sempre trata os casos em que a query retorna vazio, senão pode rolar exceção inesperada.
Erro: não colocou bloco
EXCEPTION.Se não tiver tratamento de erro no procedimento, se der exceção a transação pode travar ou quebrar a lógica.
Dica: se proteja de SQL injection.
Usa parâmetros fortemente tipados e evita SQL dinâmico se não for necessário.
Expandindo o procedimento
No mundo real, dá pra adicionar mais validações, tipo:
- Considerar descontos ou promoções pros clientes.
- Checar o limite de crédito do cliente antes de processar o pedido.
- Logar não só operações bem-sucedidas, mas também rollbacks.
GO TO FULL VERSION