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

TCL - Transaction Control Language

COMMIT, ROLLBACK, SAVEPOINT e controle de transações

📖

O que é uma Transação?

Definição

Uma transação é uma unidade lógica de trabalho que consiste em uma ou mais operações SQL que devem ser executadas como um todo indivisível.

A transação garante que todas as operações sejam concluídas com sucesso ou, em caso de falha, todas sejam desfeitas (tudo ou nada).

Propriedades ACID:

  • Atomicidade: Tudo é executado ou nada é executado
  • Consistência: O banco passa de um estado válido para outro estado válido
  • Isolamento: Transações concorrentes não interferem entre si
  • Durabilidade: Uma vez confirmada (COMMIT), a transação é permanente

Exemplo Real: Transferência Bancária

Transferir R$ 1.000 da conta A para conta B envolve duas operações:

  1. Debitar R$ 1.000 da conta A
  2. Creditar R$ 1.000 na conta B

Se a operação 1 suceder mas a 2 falhar, o dinheiro "desaparece". Uma transação garante que ambas aconteçam ou nenhuma aconteça.

Início e Fim de uma Transação no Oracle

No Oracle, uma transação:

  • Inicia automaticamente com a primeira instrução DML (INSERT, UPDATE, DELETE)
  • Termina com COMMIT (confirma) ou ROLLBACK (cancela)
  • Também termina com comandos DDL (CREATE, ALTER, DROP) - que fazem COMMIT implícito
  • Também termina ao desconectar da sessão

COMMIT - Confirmar Alterações

Como funciona o COMMIT

O comando COMMIT torna permanentes todas as alterações feitas na transação atual. Após o COMMIT:

  • As alterações são gravadas definitivamente no banco de dados
  • Outros usuários podem ver as alterações
  • Os locks (bloqueios) são liberados
  • Uma nova transação começa automaticamente
-- Exemplo básico de COMMIT
UPDATE funcionarios
SET salario = salario * 1.10
WHERE departamento_id = 10;

-- Verificar quantas linhas foram afetadas
-- (5 linhas atualizadas)

COMMIT;  -- Confirma as alterações

-- Agora as alterações são permanentes e visíveis para todos

COMMIT Implícito (Autocommit)

Alguns comandos fazem COMMIT automático no Oracle:

Comandos que causam COMMIT implícito:

  • DDL: CREATE, ALTER, DROP, TRUNCATE, RENAME
  • DCL: GRANT, REVOKE
  • Saída normal: EXIT ou desconexão normal
-- Exemplo de COMMIT implícito
UPDATE funcionarios SET salario = 5000 WHERE id = 10;  -- Transação iniciada

CREATE TABLE teste (id NUMBER);  -- DDL faz COMMIT automático!
-- A atualização acima foi confirmada automaticamente

ROLLBACK;  -- Não vai desfazer o UPDATE, pois já foi commitado!
↩️

ROLLBACK - Desfazer Alterações

Como funciona o ROLLBACK

O comando ROLLBACK desfaz todas as alterações feitas na transação atual desde o último COMMIT. Após o ROLLBACK:

  • Todas as alterações são descartadas
  • O banco retorna ao estado anterior ao início da transação
  • Os locks são liberados
  • Uma nova transação começa automaticamente
-- Exemplo básico de ROLLBACK
UPDATE funcionarios
SET salario = salario * 2
WHERE departamento_id = 10;

SELECT salario FROM funcionarios WHERE departamento_id = 10;
-- Salários dobrados (ainda não confirmado)

ROLLBACK;  -- Desfaz as alterações

SELECT salario FROM funcionarios WHERE departamento_id = 10;
-- Salários voltaram aos valores originais

Quando usar ROLLBACK

1. Erro detectado manualmente

DELETE FROM funcionarios WHERE departamento_id = 10;
-- Ops! Queria excluir apenas um funcionário!

ROLLBACK;  -- Desfaz a exclusão

2. Erro de constraint

INSERT INTO funcionarios VALUES (1, 'João', 'joao@email.com', SYSDATE, 5000, 99);
-- ORA-02291: parent key not found (departamento 99 não existe)

ROLLBACK;  -- Descarta a tentativa de inserção

3. ROLLBACK automático

O Oracle faz ROLLBACK automático se:

  • A sessão é encerrada anormalmente (crash, perda de conexão)
  • Ocorre um erro grave no sistema
🔖

SAVEPOINT - Pontos de Salvamento

O que é um SAVEPOINT?

Um SAVEPOINT é um marcador dentro de uma transação que permite fazer ROLLBACK parcial, sem desfazer toda a transação.

-- Criando savepoints
INSERT INTO departamentos VALUES (40, 'Marketing', 'Rio de Janeiro', 150000);
SAVEPOINT sp1;  -- Primeiro ponto de salvamento

UPDATE funcionarios SET salario = salario * 1.05 WHERE departamento_id = 10;
SAVEPOINT sp2;  -- Segundo ponto de salvamento

DELETE FROM funcionarios WHERE id_funcionario = 999;
SAVEPOINT sp3;  -- Terceiro ponto de salvamento

-- Desfazer apenas até sp2 (mantém INSERT e UPDATE, desfaz DELETE)
ROLLBACK TO SAVEPOINT sp2;

-- Ou desfazer até sp1 (mantém apenas INSERT)
ROLLBACK TO SAVEPOINT sp1;

-- Ou desfazer tudo
ROLLBACK;

Exemplo Prático: Processo de Migração de Dados

-- Processo complexo com múltiplos passos
BEGIN
    -- Passo 1: Criar novos registros
    INSERT INTO departamentos_novo
    SELECT * FROM departamentos_antigo;
    SAVEPOINT passo1_completo;

    -- Passo 2: Atualizar referências
    UPDATE funcionarios
    SET departamento_id = departamento_id + 1000;
    SAVEPOINT passo2_completo;

    -- Passo 3: Validar dados
    UPDATE funcionarios
    SET status = 'MIGRADO';

    -- Se algo deu errado no passo 3, voltar para passo2
    -- ROLLBACK TO SAVEPOINT passo2_completo;

    -- Se tudo OK, confirmar tudo
    COMMIT;
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK;  -- Desfaz tudo em caso de erro
        RAISE;
END;
/

Dica

Savepoints são muito úteis em procedures e processos longos onde você quer manter controle granular sobre o que foi feito, sem perder todo o trabalho em caso de erro.

🔒

Isolamento de Transações

Conceito de Isolamento

O isolamento garante que transações simultâneas não interfiram entre si. O Oracle usa locks (bloqueios) e controle de concorrência multiversão (MVCC).

Como o Oracle gerencia concorrência:

  • Leitores não bloqueiam escritores (queries não travam updates)
  • Escritores não bloqueiam leitores (updates não travam queries)
  • Escritores bloqueiam escritores (updates concorrentes na mesma linha esperam)
  • Consistência de leitura: queries veem snapshot consistente do banco

Exemplo de Concorrência

Sessão 1

-- T1: Iniciar transação
UPDATE funcionarios
SET salario = 6000
WHERE id = 10;

-- Aguardando...
-- (não fez COMMIT ainda)


-- T3: Confirmar
COMMIT;

Sessão 2

-- T2: Tentar atualizar
-- mesma linha
UPDATE funcionarios
SET salario = 7000
WHERE id = 10;

-- ESPERA! Sessão 1 tem lock

-- T4: Após COMMIT da Sessão 1
-- Agora executa e pega lock

Observação Importante

Enquanto isso, outras sessões fazendo SELECT na linha veem o valor ANTIGO (antes do UPDATE), garantindo consistência de leitura. Após o COMMIT, elas veem o novo valor.

🛠️

Exemplos Práticos Completos

Exemplo 1: Transferência entre Contas

-- Transferir R$ 1000 da conta 100 para conta 200
SAVEPOINT inicio_transferencia;

-- Debitar conta origem
UPDATE contas
SET saldo = saldo - 1000
WHERE numero_conta = 100;

-- Verificar se ficou negativo
SELECT saldo INTO v_saldo
FROM contas
WHERE numero_conta = 100;

IF v_saldo < 0 THEN
    ROLLBACK TO SAVEPOINT inicio_transferencia;
    RAISE_APPLICATION_ERROR(-20001, 'Saldo insuficiente');
END IF;

-- Creditar conta destino
UPDATE contas
SET saldo = saldo + 1000
WHERE numero_conta = 200;

-- Registrar histórico
INSERT INTO historico_transferencias VALUES
    (seq_transf.NEXTVAL, 100, 200, 1000, SYSDATE);

-- Confirmar toda a transação
COMMIT;

Exemplo 2: Importação de Dados com Validação

-- Importar dados em lote com validação
DECLARE
    v_erros NUMBER := 0;
BEGIN
    SAVEPOINT inicio_importacao;

    -- Inserir dados temporários
    INSERT INTO funcionarios_temp
    SELECT * FROM arquivo_externo;

    -- Validar dados
    SELECT COUNT(*) INTO v_erros
    FROM funcionarios_temp
    WHERE email NOT LIKE '%@%'
       OR salario <= 0
       OR departamento_id NOT IN (SELECT id_departamento FROM departamentos);

    IF v_erros > 0 THEN
        DBMS_OUTPUT.PUT_LINE('Encontrados ' || v_erros || ' registros inválidos');
        ROLLBACK TO SAVEPOINT inicio_importacao;
    ELSE
        -- Mover dados validados para tabela final
        INSERT INTO funcionarios
        SELECT * FROM funcionarios_temp;

        COMMIT;
        DBMS_OUTPUT.PUT_LINE('Importação concluída com sucesso');
    END IF;
END;
/

Exemplo 3: Atualização com Auditoria

-- Atualizar salários com registro de auditoria
BEGIN
    -- Registrar estado anterior
    INSERT INTO auditoria_salarios
    SELECT id_funcionario, salario, USER, SYSDATE, 'ANTES'
    FROM funcionarios
    WHERE departamento_id = 10;

    SAVEPOINT antes_atualizacao;

    -- Aplicar aumento
    UPDATE funcionarios
    SET salario = salario * 1.10
    WHERE departamento_id = 10;

    -- Registrar estado posterior
    INSERT INTO auditoria_salarios
    SELECT id_funcionario, salario, USER, SYSDATE, 'DEPOIS'
    FROM funcionarios
    WHERE departamento_id = 10;

    -- Verificar se ultrapassou orçamento
    SELECT SUM(salario) INTO v_total
    FROM funcionarios
    WHERE departamento_id = 10;

    IF v_total > v_orcamento_depto THEN
        ROLLBACK TO SAVEPOINT antes_atualizacao;
        RAISE_APPLICATION_ERROR(-20002, 'Orçamento ultrapassado');
    ELSE
        COMMIT;
    END IF;
END;
/

Resultado Esperado

O que você deve dominar após este tópico:

Entender transações

Conceito de transação e propriedades ACID

Usar COMMIT corretamente

Saber quando confirmar alterações

Usar ROLLBACK efetivamente

Desfazer alterações quando necessário

Criar SAVEPOINTs

Controlar transações complexas com pontos intermediários

Entender isolamento

Conceitos de locks e concorrência

Evitar commits implícitos

Conhecer comandos que fazem autocommit

📝

Exercícios Práticos

Exercício 1: Simular Erro e Rollback

Execute uma série de INSERTs em uma tabela, depois faça ROLLBACK e verifique que os dados não foram salvos.

Ver Solução
-- Inserir dados de teste
INSERT INTO funcionarios VALUES (1000, 'Teste 1', 'teste1@email.com', SYSDATE, 3000, 10);
INSERT INTO funcionarios VALUES (1001, 'Teste 2', 'teste2@email.com', SYSDATE, 3500, 10);

-- Verificar inserção
SELECT * FROM funcionarios WHERE id_funcionario >= 1000;
-- (2 linhas retornadas)

-- Desfazer
ROLLBACK;

-- Verificar que não foram salvos
SELECT * FROM funcionarios WHERE id_funcionario >= 1000;
-- (0 linhas - dados desapareceram)

Exercício 2: Usar SAVEPOINTs

Crie 3 savepoints em uma transação e faça rollback parcial para diferentes pontos.

Ver Solução
-- Passo 1
INSERT INTO departamentos VALUES (50, 'Vendas', 'Curitiba', 180000);
SAVEPOINT passo1;

-- Passo 2
INSERT INTO departamentos VALUES (60, 'Suporte', 'Brasília', 120000);
SAVEPOINT passo2;

-- Passo 3
INSERT INTO departamentos VALUES (70, 'Logística', 'Manaus', 100000);
SAVEPOINT passo3;

-- Desfazer apenas passo 3
ROLLBACK TO SAVEPOINT passo2;
SELECT * FROM departamentos WHERE id_departamento >= 50;
-- (Apenas 50 e 60 existem)

-- Confirmar
COMMIT;

Exercício 3: Transação Bancária

Implemente uma transferência entre duas contas com validação de saldo e rollback em caso de erro.

Ver Solução
DECLARE
    v_saldo_origem NUMBER;
    v_valor_transf NUMBER := 500;
BEGIN
    SAVEPOINT inicio_transf;

    -- Verificar saldo
    SELECT saldo INTO v_saldo_origem
    FROM contas
    WHERE numero_conta = 1001;

    IF v_saldo_origem < v_valor_transf THEN
        RAISE_APPLICATION_ERROR(-20001, 'Saldo insuficiente');
    END IF;

    -- Débito
    UPDATE contas
    SET saldo = saldo - v_valor_transf
    WHERE numero_conta = 1001;

    -- Crédito
    UPDATE contas
    SET saldo = saldo + v_valor_transf
    WHERE numero_conta = 1002;

    COMMIT;
    DBMS_OUTPUT.PUT_LINE('Transferência realizada com sucesso');
EXCEPTION
    WHEN OTHERS THEN
        ROLLBACK TO SAVEPOINT inicio_transf;
        DBMS_OUTPUT.PUT_LINE('Erro: ' || SQLERRM);
END;
/
Anterior: SELECT Avançado Próximo: Funções SQL