Por padrão, as Transações, na maioria das vezes, não são revertidas / canceladas automaticamente quando ocorre um erro. Isso geralmente não é um problema, desde que você tenha um tratamento de erros adequado e ligue para ROLLBACK
si mesmo. No entanto, às vezes as coisas ficam complicadas, como no caso de erros de interrupção de lote ou ao usar OPENQUERY
(ou Servidores Vinculados em geral) e ocorre um erro no sistema remoto. Embora a maioria dos erros possa ser interceptada TRY...CATCH
, existem dois que não podem ser interceptados dessa maneira (embora não se lembre de quais no momento - pesquisando). Nesses casos, você deve usar SET XACT_ABORT ON
para reverter corretamente a transação.
SET XACT_ABORT ON faz o SQL Server reverter imediatamente qualquer transação (se uma estiver ativa) e abortar o lote se ocorrer algum erro. Essa configuração existia antes do SQL Server 2005, que introduziu a TRY...CATCH
construção. Na maioria das vezes, TRY...CATCH
lida com a maioria das situações e, portanto, obsoleta a necessidade XACT_ABORT ON
. No entanto, ao usar OPENQUERY
(e possivelmente um outro cenário que não me lembro no momento), você ainda precisará usá-lo SET XACT_ABORT ON;
.
Você sempre deve ter o tratamento de erros adequado, especialmente ao usar Transações. A TRY...CATCH
construção, introduzida no SQL Server 2005, fornece um meio de lidar com quase todas as situações, uma melhoria bem-vinda sobre o teste @@ERROR
após cada instrução, o que não ajudou muito com erros de interrupção de lote.
TRY...CATCH
introduziu um novo "estado", no entanto. Quando nãoTRY...CATCH
estiver usando a construção, se você tiver uma transação ativa e ocorrer um erro, existem vários caminhos que podem ser seguidos:
XACT_ABORT OFF
e erro de cancelamento de instrução: a transação ainda está ativa e o processamento continua com a próxima instrução , se houver.
XACT_ABORT OFF
e a maioria dos erros de interrupção de lote: a transação ainda está ativa e o processamento continua com o próximo lote , se houver.
XACT_ABORT OFF
e certos erros de interrupção de lote: a transação é revertida e o processamento continua com o próximo lote , se houver.
XACT_ABORT ON
e qualquer erro: a transação é revertida e o processamento continua com o próximo lote , se houver.
NO ENTANTO, ao usar TRY...CATCH
, os erros de interrupção de lote não abortam o lote, mas transferem o controle para o CATCH
bloco. Quando XACT_ABORT
for OFF
, a Transação ainda estará ativa na grande maioria das vezes, e você precisará COMMIT
, ou provavelmente ROLLBACK
. Porém, ao encontrar certos erros de interrupção de lote (como com OPENQUERY
), ou quando XACT_ABORT
estiver ON
, a Transação estará em um novo estado, "não comprometível". Nesse estado, você não pode COMMIT
, nem pode executar operações DML. Tudo o que você pode fazer é ROLLBACK
e SELECT
declarações. No entanto, nesse estado "incompatível", a Transação foi revertida após o erro que ocorreu e emitir a ROLLBACK
é apenas uma formalidade, mas que deve ser executada.
Uma função, XACT_STATE , pode ser usada para determinar se uma transação está ativa, incompatível ou não existe. Recomenda-se (por alguns, pelo menos) verificar esta função no CATCH
bloco para determinar se o resultado é -1
(ou seja, incompatível) em vez de testar se @@TRANCOUNT > 0
. Mas com XACT_ABORT ON
, esse deve ser o único estado possível, portanto parece que testar @@TRANCOUNT > 0
e XACT_STATE() <> 0
é equivalente. Por outro lado, quando XACT_ABORT
existe OFF
e existe uma transação ativa, é possível ter um estado de um 1
ou -1
de um CATCH
bloco, o que permite a possibilidade de emitir em COMMIT
vez de ROLLBACK
(embora, não consigo pensar em um caso para quando alguém gostaria deCOMMIT
se a transação for confirmada). Mais informações e pesquisas sobre o uso XACT_STATE()
em um CATCH
bloco XACT_ABORT ON
podem ser encontradas na minha resposta à seguinte pergunta do DBA.SE: Em que casos uma transação pode ser confirmada de dentro do bloco CATCH quando XACT_ABORT está definido como ON? . Observe que há um pequeno erro XACT_STATE()
que faz com que ele retorne falsamente 1
em certos cenários: XACT_STATE () retorna 1 quando usado em SELECT com algumas variáveis do sistema, mas sem a cláusula FROM
spNewBilling3
gera um erro, mas você não deseja reverterspNewBilling2
ouspNewBilling1
, basta remover[begin|rollback|commit] transaction createSavebillinginvoice
despSavesomename
.