CodeGym /Cursos /SQL SELF /Exemplo de procedimento complexo para processar pedidos: ...

Exemplo de procedimento complexo para processar pedidos: validação de dados, atualização de status, logging

SQL SELF
Nível 54 , Lição 0
Disponível

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:

  1. Checar se o produto necessário está disponível no estoque.
  2. Se tiver produto suficiente, dar baixa na quantidade no estoque.
  3. Atualizar o status do pedido pra "Processado".
  4. Registrar a informação da operação bem-sucedida no log.
  5. 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:

  1. Checar se o produto pedido existe no estoque e se tem quantidade suficiente.
  2. Se tiver produto suficiente, diminuir a quantidade na tabela inventory.
  3. Mudar o status do pedido pra "Processado".
  4. Registrar o sucesso na tabela order_logs.
  5. 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.

  1. 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.

  2. Etapa do estoque:

    se tiver produto suficiente, diminui a quantidade no estoque. Isso é feito com UPDATE.

  3. Etapa de mudança de status do pedido:

    muda o status pra "Processed" (Processado) pra indicar que o pedido foi finalizado de boa.

  4. Etapa de logging:

    depois de processar o pedido com sucesso, adiciona uma mensagem na tabela order_logs pra guardar a info da operação.

  5. 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

  1. Erro: esqueceu de checar NOT FOUND depois do SELECT INTO.

    Sempre trata os casos em que a query retorna vazio, senão pode rolar exceção inesperada.

  2. 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.

  3. 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.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION