Transações no SQL Server: do básico ao XACT_ABORT, TRY…CATCH e níveis de isolamento

Introdução

Transações são a base da integridade em bancos de dados. Quando bem definidas, garantem as propriedades ACID: Atomicidade, Consistência, Isolamento e Durabilidade. Neste guia prático você verá, na prática, os tipos de transação no SQL Server (autocommit, implícita e explícita), como evitar commits “parciais” com XACT_ABORT, como tratar erros corretamente com TRY…CATCH, RAISERROR e THROW, além de entender os impactos de isolamento/concorrência e por que leituras “sem bloqueio” exigem critério.

Ambiente de demonstração

A tabela a seguir será usada em todos os exemplos:

DROP TABLE IF exists Funcionario
go
CREATE TABLE Funcionario (PK int primary key, Nome varchar(50), Descricao varchar(100), [Status] char(1),Salario decimal(10,2))
INSERT Funcionario VALUES (1,'Fernando','Gerente','B',5600.00)
INSERT Funcionario VALUES (2,'Ana Maria','Diretor','A',7500.00)
INSERT Funcionario VALUES (3,'Lucia','Gerente','B',5600.00)
INSERT Funcionario VALUES (4,'Pedro','Operacional','C',2600.00)
INSERT Funcionario VALUES (5,'Carlos','Diretor','A',7500.00)
INSERT Funcionario VALUES (6,'Carol','Operacional','C',2600.00)
INSERT Funcionario VALUES (7,'Luana','Operacional','C',9600.00)
INSERT Funcionario VALUES (8,'Paulo','Diretor','A',7500.00)
INSERT Funcionario VALUES (9,'Erick','Operacional','C',2600.00)
INSERT Funcionario VALUES (10,'Joana','Operacional','C',2600.00)
go

1) Tipos de transações

1.1 Autocommit (automáticas)

Cada instrução é uma transação independente; se não ocorrer erro, o commit é automático. Observe que ROLLBACK isolado não desfaz o UPDATE anterior, pois não há transação aberta:

-- Autocommit
UPDATE Funcionario SET Salario = 9300.00 WHERE PK = 2; -- valor original 7500
ROLLBACK;  -- não tem efeito aqui

-- Em outra conexão, verifique:
SELECT * FROM Funcionario WHERE PK = 2;

1.2 Transações implícitas

Ao ativar SET IMPLICIT_TRANSACTIONS ON, o SQL Server passa a abrir uma transação implicitamente na primeira instrução DML de cada “lote” de ações — e você precisa dar COMMIT ou ROLLBACK.

SET IMPLICIT_TRANSACTIONS ON;

UPDATE Funcionario SET Salario = 9000.00 WHERE PK = 5; -- 7500
UPDATE Funcionario SET Salario = 5300.00 WHERE PK = 6; -- 2600

ROLLBACK;

-- Em outra conexão:
SELECT * FROM Funcionario WHERE PK = 5;
SELECT * FROM Funcionario WHERE PK = 6;
SELECT * FROM Funcionario WHERE PK = 10;

SET IMPLICIT_TRANSACTIONS OFF;

1.3 Transações explícitas

Sem tratamento de erro, é fácil “commitar pela metade”. Abaixo, o UPDATE executa, o INSERT falha (chave primária duplicada) e, se você der COMMIT depois, confirmará apenas o que deu certo — péssimo para integridade:

-- Transação explícita SEM tratamento de erro
SELECT * FROM Funcionario WHERE PK IN (9,10);
SELECT @@TRANCOUNT;

BEGIN TRAN;
  UPDATE Funcionario SET Salario = 3000.00 WHERE PK = 9; -- 2600
  INSERT Funcionario VALUES (10,'Joana','Operacional','C',2600.00); -- ERRO PK
COMMIT;  -- ⚠️ confirma o UPDATE, embora o INSERT tenha falhado

2) Evite commits parciais com XACT_ABORT

Quando XACT_ABORT está ON, qualquer erro de tempo de execução aborta toda a transação automaticamente com rollback — ideal para garantir atomicidade em blocos críticos. Contudo toda a transação precisa estar no mesmo Batch!

SELECT * FROM Funcionario WHERE PK IN (9,10);
SELECT @@TRANCOUNT;

SET XACT_ABORT ON;
BEGIN TRAN;
  UPDATE Funcionario SET Salario = 8500.00 WHERE PK = 9; -- 3000
  INSERT Funcionario VALUES (10,'Joana','Operacional','C',2600.00); -- ERRO PK
COMMIT;
SET XACT_ABORT OFF;

Mes cuidado, Nem todos os erros disparam rollback automático

  • XACT_ABORT ON funciona apenas para erros de tempo de execução (runtime errors), como violação de chave primária, divisão por zero, truncamento de dados etc.

  • Já erros de compilação (syntax error, coluna inexistente, etc.) não são afetados, pois a transação nem chega a ser executada.

  • Também há exceções conhecidas, como alguns erros de constraint deferred ou warnings que não abortam a transação.

  • Dentro de triggers, dependendo da operação, XACT_ABORT pode não funcionar como esperado, pois o contexto transacional é herdado da operação que disparou a trigger.

3) Tratamento de erros profissional: TRY…CATCH, RAISERROR e THROW

3.1 TRY…CATCH com transações

Padrão recomendável: abrir a transação dentro do TRY, e no CATCH sempre checar @@TRANCOUNT para garantir o ROLLBACK se algo falhar:

SELECT * FROM Funcionario WHERE PK IN (8,10);

BEGIN TRY
  BEGIN TRAN;
    UPDATE Funcionario SET Salario = 9000.00 WHERE PK = 8; -- 7500
    INSERT Funcionario VALUES (10,'Joana','Operacional','C',2600.00); -- ERRO PK
  COMMIT;
END TRY
BEGIN CATCH
  SELECT ERROR_NUMBER(), ERROR_MESSAGE();
  IF @@TRANCOUNT > 0
    ROLLBACK;
END CATCH;
GO

3.2 Severidades

Para investigar e padronizar mensagens, você pode consultar master.sys.messages e entender a gravidade (severity) dos erros:

SELECT * FROM master.sys.messages;
SELECT DISTINCT severity FROM master.sys.messages ORDER BY 1;

/*
<= 16   - Informativas
17      - Recursos esgotados (ex.: disco cheio ou blocking excessivo)
18–19   - Erros não fatais (conexão permanece)
20–24   - Erros fatais (derrubam a conexão)
*/

3.3 RAISERROR

Dentro do CATCH, você pode levantar um aviso customizado (útil para logs/monitoramento) informando contexto do erro:

BEGIN TRY
  SELECT 10/0 AS 'Provoca erro de divisão por zero.';
END TRY
BEGIN CATCH
  DECLARE @Banco nvarchar(200) = DB_NAME();
  RAISERROR (N'Erro de divisão por zero no Banco de Dados %s', 10, 1, @Banco);
END CATCH;
GO

3.4 THROW

Use THROW para propagar o erro original ou para lançar um erro de aplicativo (por exemplo, código 51000) com mensagem clara:

-- Repassa o erro original:
BEGIN TRY
  SELECT 10/0;
END TRY
BEGIN CATCH
  THROW;
END CATCH;
GO

-- Lança um erro específico:
BEGIN TRY
  SELECT 10/0;
END TRY
BEGIN CATCH
  DECLARE @Erro nvarchar(200) = N'Erro de divisão por zero no Banco de Dados ' + DB_NAME();
  ;THROW 51000, @Erro, 1;
END CATCH;

4) Concorrência e isolamento: o que considerar

Por padrão (READ COMMITTED), quem escreve bloqueia quem lê e vice-versa: durante um UPDATE, um SELECT concorrente fica em blocking até a liberação do lock. É seguro para os dados, porém pode reduzir a escalabilidade sob alta concorrência.

WITH (NOLOCK)/READ UNCOMMITTED eliminam o lock de leitura, mas abrem espaço para leitura suja (dirty read): você pode ler um valor que nunca será persistido se a outra transação fizer rollback. Só use com plena consciência das regras de negócio. Alternativamente, o snapshot isolation (com versionamento de linha) permite leitura sem bloqueio entregando o último valor confirmado — ótimo para relatórios/consultas — mas exige habilitação no banco e análise de impacto pelo DBA.

Deadlocks não são a mesma coisa que blocking: são ciclos de dependência sem saída; o SQL Server escolhe uma vítima (a de menor “custo” para desfazer) e a aborta para liberar o caminho. Boas práticas de acesso ordenado aos recursos e índices adequados ajudam a mitigá-los.

5) Boas práticas resumidas

  • Padronize: em código transacional, use TRY…CATCH + @@TRANCOUNT e considere XACT_ABORT ON.

  • Seja explícito sobre isolamento: padrão (READ COMMITTED) é seguro; snapshot ajuda relatórios; evite NOLOCK como “padrão da casa”.

  • Teste em concorrência: valide comportamento sob carga (leituras repetidas, transações longas, etc.).

  • Monitore: mensagens de erro, severidades e contadores de locking/waits ajudam a diagnosticar gargalos.

Conclusão

Com uma estratégia clara — transações bem definidas, tratamento de erro robusto e isolamento adequado — você garante integridade e previsibilidade sem abrir mão de desempenho. Use os blocos de código deste artigo como ponto de partida para padronizar suas rotinas T-SQL.

Fui, mas volto com mais SQL Server em breve!

✍️ Sobre o autor
O Prof. Landry é especialista em Microsoft SQL Server desde 1999, Microsoft Trainer, Professor Universitário e criador do canal SQL Server Expert no YouTube, com conteúdo técnico semanal para DBAs e profissionais de dados.

🚀 Quer aprender mais sobre SQL Server?
👉 Me acompanhe no LinkedIn e inscreva-se no canal para não perder nenhuma dica prática!

0
Subscribe to my newsletter

Read articles from SQL Server Expert directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

SQL Server Expert
SQL Server Expert

O Prof. Landry Duailibe é especialista em Microsoft SQL Server desde 1999, Microsoft Certified Trainer, professor universitário e criador do canal SQL Server Expert no YouTube, onde compartilha conteúdo técnico semanal para DBAs e profissionais de dados. Já ajudou milhares de alunos a evoluírem suas habilidades com SQL Server e conquistarem melhores oportunidades na área de dados.