Qual é o benefício de usar SET XACT_ABORT ONem um procedimento armazenado?
Qual é o benefício de usar SET XACT_ABORT ONem um procedimento armazenado?
Respostas:
SET XACT_ABORT ONinstrui o SQL Server a reverter toda a transação e abortar o lote quando ocorrer um erro em tempo de execução. Ele cobre você em casos como um tempo limite de comando que ocorre no aplicativo cliente, e não no próprio SQL Server (que não é coberto pela XACT_ABORT OFFconfiguração padrão ).
Como o tempo limite da consulta deixará a transação aberta, SET XACT_ABORT ONé recomendado em todos os procedimentos armazenados com transações explícitas (a menos que você tenha um motivo específico para fazer outra coisa), pois as conseqüências de um aplicativo que executa o trabalho em uma conexão com uma transação aberta são desastrosas.
Há uma excelente visão geral no blog de Dan Guzman ,
BEGIN TRY- BEGIN CATCHe ROLLBACKcom o BEGIN CATCHbloco no Sql?
BEGIN TRY- BEGIN CATCHnão captura coisas como um tempo limite no aplicativo cliente, e alguns erros SQL também são impossíveis de rastrear , deixando uma transação aberta em que você não esperaria uma.
Na minha opinião, SET XACT_ABORT ON foi tornado obsoleto pela adição de BEGIN TRY / BEGIN CATCH no SQL 2k5. Antes dos blocos de exceção no Transact-SQL, era realmente difícil lidar com erros e os procedimentos desequilibrados eram muito comuns (procedimentos que tinham uma @@ TRANCOUNT diferente na saída em comparação à entrada).
Com a adição do tratamento de exceção Transact-SQL, é muito mais fácil escrever procedimentos corretos que garantem o equilíbrio adequado das transações. Por exemplo, eu uso este modelo para manipulação de exceção e transações aninhadas :
create procedure [usp_my_procedure_name]
as
begin
set nocount on;
declare @trancount int;
set @trancount = @@trancount;
begin try
if @trancount = 0
begin transaction
else
save transaction usp_my_procedure_name;
-- Do the actual work here
lbexit:
if @trancount = 0
commit;
end try
begin catch
declare @error int, @message varchar(4000), @xstate int;
select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
if @xstate = -1
rollback;
if @xstate = 1 and @trancount = 0
rollback
if @xstate = 1 and @trancount > 0
rollback transaction usp_my_procedure_name;
raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
end catch
end
go
Isso me permite escrever procedimentos atômicos que revertem apenas seu próprio trabalho em caso de erros recuperáveis.
Um dos principais problemas enfrentados pelos procedimentos Transact-SQL é a pureza dos dados : algumas vezes, os parâmetros recebidos ou os dados nas tabelas estão completamente errados, resultando em erros de chave duplicados, erros de restrição referenciais, erros de verificação de restrição e assim por diante. Afinal, esse é exatamente o papel dessas restrições, se esses erros de pureza dos dados fossem impossíveis e todos capturados pela lógica de negócios, as restrições seriam todas obsoletas (exagero dramático adicionado para efeito). Se XACT_ABORT estiver ativado, todos esses erros resultarão na perda de toda a transação, em vez de poder codificar blocos de exceção que manipulam a exceção normalmente. Um exemplo típico é tentar fazer um INSERT e reverter para uma violação UPDATE on PK.
Citando o MSDN :
Quando SET XACT_ABORT está ativado, se uma instrução Transact-SQL gerar um erro em tempo de execução, a transação inteira será encerrada e revertida. Quando SET XACT_ABORT está desativado, em alguns casos, apenas a instrução Transact-SQL que gerou o erro é revertida e a transação continua o processamento.
Na prática, isso significa que algumas das instruções podem falhar, deixando a transação 'parcialmente concluída', e pode não haver sinal dessa falha para um chamador.
Um exemplo simples:
INSERT INTO t1 VALUES (1/0)
INSERT INTO t2 VALUES (1/1)
SELECT 'Everything is fine'
Esse código seria executado 'com êxito' com XACT_ABORT OFF e terminará com um erro com XACT_ABORT ON ('INSERT INTO t2' não será executado e um aplicativo cliente gerará uma exceção).
Como uma abordagem mais flexível, você pode verificar @@ ERROR após cada instrução (old school) ou usar os blocos TRY ... CATCH (MSSQL2005 +). Pessoalmente, prefiro definir XACT_ABORT ON sempre que não houver motivo para algum tratamento avançado de erros.
Com relação aos tempos limite do cliente e ao uso de XACT_ABORT para manipulá-los, na minha opinião, há pelo menos um motivo muito bom para ter tempos limites nas APIs do cliente, como SqlClient, e que é para proteger o código do aplicativo cliente contra conflitos que ocorrem no código do servidor SQL. Nesse caso, o código do cliente não tem falhas, mas deve se proteger de um bloqueio permanente, aguardando a conclusão do comando no servidor. Por outro lado, se houver um tempo limite do cliente para proteger o código do cliente, o XACT_ABORT ON precisará proteger o código do servidor contra as interrupções do cliente, caso o código do servidor demore mais para ser executado do que o cliente está disposto a esperar.