CodeGym /Cursos /SQL SELF /Exemplo de uso de transações em cenários reais

Exemplo de uso de transações em cenários reais

SQL SELF
Nível 39, Lição 3
Disponível

Às vezes, transações parecem aqueles personagens de filme de super-herói. Elas salvam nossos bancos de dados do desastre quando rola algum erro, falha ou bug. Se você tá lidando com uma tarefa que precisa de várias operações que não podem ser separadas, as transações garantem que tudo vai rolar junto. Bora ver como isso funciona com um exemplo de processamento de pagamento.

Processando pagamentos

Imagina aquela situação clássica: você tem duas contas bancárias e quer transferir dinheiro de uma pra outra. Não é só apertar um botão e pronto. Tem que garantir que a grana saiu certinho de uma conta e entrou na outra. Qualquer erro pode dar ruim: ou as duas contas ficam sem alteração, ou o saldo fica zoado (tipo, o dinheiro some ou aparece "do nada").

Cenário: Transferência de dinheiro entre contas

Olha só nosso código. Lê com atenção, tipo mensagem de uma galáxia distante:

-- Começando a transação
BEGIN;

-- Passo 1. Tirando dinheiro da conta do remetente
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;

-- Passo 2. Adicionando dinheiro na conta do destinatário
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;

-- Deu tudo certo? Então salva as mudanças!
COMMIT;

O que é importante aqui?

  • Se no Passo 1 ou Passo 2 der ruim (tipo erro na query, saldo insuficiente), a transação pode ser desfeita com ROLLBACK, e os dados ficam como estavam antes.
  • COMMIT garante que as mudanças só vão ser aplicadas se TODOS os passos derem certo.

Adicionando verificação de saldo

E se o remetente não tiver saldo suficiente pra transferência? Bora colocar uma checagem de saldo pra não deixar ele "no vermelho" sem querer.

-- Começando a transação
BEGIN;

-- Pegando o saldo atual do remetente
DO $$
DECLARE
    current_balance NUMERIC;
BEGIN
    SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;

    -- Checando se tem grana suficiente
    IF current_balance >= 100 THEN
        -- Se tiver, faz a transferência
        UPDATE accounts
        SET balance = balance - 100
        WHERE account_id = 1;

        UPDATE accounts
        SET balance = balance + 100
        WHERE account_id = 2;

        -- Salva as mudanças
        COMMIT;
    ELSE
        -- Se não tiver, desfaz tudo
        ROLLBACK;
        RAISE NOTICE 'Saldo insuficiente para transferência!';
    END IF;
END $$;

O que ficou mais interessante aqui?

  • A gente usa um bloco PL/pgSQL com checagem usando IF. Se o saldo for menor que o necessário, a transação é recusada e nada muda.
  • ROLLBACK desfaz as mudanças se já tiver começado alguma (nesse caso, nem começou, mas é uma boa prática).
Importante!

Essa aula é sobre cenários reais de uso de transações, então resolvi trazer um exemplo da vida real. Ele tem uma stored procedure e tá escrito em PL-SQL. Acho que você já tem experiência suficiente pra sacar como tudo funciona aqui. No futuro, a gente volta pra PL-SQL e encara exemplos ainda mais cabulosos.

Atualização em massa de dados numa transação

Criar uma transação não serve só pra transferir dinheiro. Imagina que você tem o banco de dados de uma loja online, onde todo dia vários pedidos mudam de status, tipo de "em entrega" pra "finalizado". Como atualizar vários registros de uma vez só e ainda garantir que, se der ruim, dá pra desfazer tudo? Claro, usando transação.

Bora ver outro cenário: atualização de status dos pedidos.

Olha o exemplo:

-- Começando a transação
BEGIN;

-- Passo 1. Atualizando pedidos com data de entrega já passada
UPDATE orders
SET status = 'completed'
WHERE delivery_date < CURRENT_DATE;

-- Passo 2. Avisando que deu tudo certo
RAISE NOTICE 'Todos os status dos pedidos foram atualizados com sucesso.';

-- Salva as mudanças
COMMIT;

E se der ruim?

Sempre pode rolar um erro. Tipo, você esqueceu de colocar o WHERE e agora todos os pedidos mudaram pra completed. Pra evitar isso, é importante finalizar a transação ou desfazer ela na hora.

Olha o cenário com rollback:

-- Começando a transação
BEGIN;

-- Passo 1. Tentando atualizar pedidos sem condição (ih, erro!)
UPDATE orders
SET status = 'completed';

-- Desfazendo a transação por causa do erro
ROLLBACK;

-- Agora os pedidos ficaram como estavam

Deixando mais "flexível" com SAVEPOINT

Nem sempre você quer desfazer a transação toda. Se seu cenário tem vários passos, talvez você queira desfazer só um deles. É aí que entra o SAVEPOINT.

Agora o cenário é: processar vários passos com a opção de desfazer só um deles.

Imagina que você tá processando um pedido com vários passos: tirando produto do estoque, atualizando status do pedido, mandando notificação pro cliente. Se a notificação não for enviada, você quer desfazer só esse passo, mas manter o resto.

-- Começar a transação
BEGIN;

-- Passo 1. Tirando produto do estoque
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;

-- Salvando ponto de rollback
SAVEPOINT step1;

-- Passo 2. Atualizando status do pedido
UPDATE orders
SET status = 'shipped'
WHERE order_id = 202;

-- Tentando mandar notificação pro cliente
SAVEPOINT step2;
-- Ih, erro na notificação!
ROLLBACK TO SAVEPOINT step2;

-- Decide que é seguro finalizar a transação
COMMIT;

Conclusão

Transações não são só uma ferramenta técnica, são a garantia da integridade dos seus dados. Elas protegem contra o "efeito dominó", quando um erro pode derrubar o sistema todo. Sempre que você fizer várias operações conectadas, se pergunte: "E se uma delas falhar?" Se a resposta for "catástrofe", é hora de usar transação. Lembra: melhor gastar uns minutos escrevendo uma transação do que horas recuperando dados depois de um bug. Seus usuários (e seus nervos) vão agradecer!

2
Tarefa
SQL SELF, nível 39, lição 3
Bloqueado
Atualização em massa dos status dos pedidos
Atualização em massa dos status dos pedidos
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION