sql - try - xact_abort para que sirve
¿Por qué Sql Server sigue ejecutándose después de raiserror cuando xact_abort está activado? (4)
Como se señaló en MSDN , se debe usar la instrucción THROW
lugar de RAISERROR
.
Los dos se comportan de forma ligeramente diferente . Pero cuando XACT_ABORT
está configurado en ON, entonces siempre debe usar el comando THROW
.
Me sorprendió algo en TSQL. Pensé que si xact_abort estaba activado, llamaría algo así como
raiserror(''Something bad happened'', 16, 1);
detendría la ejecución del procedimiento almacenado (o cualquier lote).
Pero mi mensaje de error ADO.NET acaba de demostrar lo contrario. Recibí tanto el mensaje de error de error del mensaje de excepción, como el siguiente mensaje que se rompió después de eso.
Esta es mi solución (que es mi hábito de todos modos), pero no parece que sea necesario:
if @somethingBadHappened
begin;
raiserror(''Something bad happened'', 16, 1);
return;
end;
Los documentos dicen esto:
Cuando SET XACT_ABORT está activado, si una instrucción de Transact-SQL genera un error de tiempo de ejecución, la transacción completa finaliza y se retrotrae.
¿Eso significa que debo estar usando una transacción explícita?
Esto es por diseño TM , como puede ver en la respuesta del equipo Connect by SQL Server a una pregunta similar:
Gracias por tus comentarios. Por diseño, la opción de conjunto XACT_ABORT no afecta el comportamiento de la instrucción RAISERROR. Consideraremos sus comentarios para modificar este comportamiento para una versión futura de SQL Server.
Sí, esto es un problema para algunos que esperaban que RAISERROR
con una gran gravedad (como 16
) fuera igual a un error de ejecución de SQL, no lo es.
Su solución alternativa es solo lo que debe hacer, y el uso de una transacción explícita no tiene ningún efecto en el comportamiento que desea cambiar.
Si usa un bloque try / catch, un número de error de error de gravedad de 11-19 hará que la ejecución salte al bloque catch.
Cualquier gravedad superior a 16 es un error del sistema. Para demostrar el siguiente código establece un bloque try / catch y ejecuta un procedimiento almacenado que suponemos que fallará:
supongamos que tenemos una tabla [dbo]. [Errores] para contener errores supongamos que tenemos un procedimiento almacenado [dbo]. [AssumeThisFails] que fallará cuando lo ejecutemos
-- first lets build a temporary table to hold errors
if (object_id(''tempdb..#RAISERRORS'') is null)
create table #RAISERRORS (ErrorNumber int, ErrorMessage varchar(400), ErrorSeverity int, ErrorState int, ErrorLine int, ErrorProcedure varchar(128));
-- this will determine if the transaction level of the query to programatically determine if we need to begin a new transaction or create a save point to rollback to
declare @tc as int;
set @tc = @@trancount;
if (@tc = 0)
begin transaction;
else
save transaction myTransaction;
-- the code in the try block will be executed
begin try
declare @return_value = ''0'';
set @return_value = ''0'';
declare
@ErrorNumber as int,
@ErrorMessage as varchar(400),
@ErrorSeverity as int,
@ErrorState as int,
@ErrorLine as int,
@ErrorProcedure as varchar(128);
-- assume that this procedure fails...
exec @return_value = [dbo].[AssumeThisFails]
if (@return_value <> 0)
raiserror(''This is my error message'', 17, 1);
-- the error severity of 17 will be considered a system error execution of this query will skip the following statements and resume at the begin catch block
if (@tc = 0)
commit transaction;
return(0);
end try
-- the code in the catch block will be executed on raiserror("message", 17, 1)
begin catch
select
@ErrorNumber = ERROR_NUMBER(),
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ERROR_PROCEDURE();
insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
-- if i started the transaction
if (@tc = 0)
begin
if (XACT_STATE() <> 0)
begin
select * from #RAISERRORS;
rollback transaction;
insert into [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
select * from #RAISERRORS;
insert [dbo].[Errors] (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
return(1);
end
end
-- if i didn''t start the transaction
if (XACT_STATE() = 1)
begin
rollback transaction myTransaction;
if (object_id(''tempdb..#RAISERRORS'') is not null)
insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
else
raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
return(2);
end
else if (XACT_STATE() = -1)
begin
rollback transaction;
if (object_id(''tempdb..#RAISERRORS'') is not null)
insert #RAISERRORS (ErrorNumber, ErrorMessage, ErrorSeverity, ErrorState, ErrorLine, ErrorProcedure)
values (@ErrorNumber, @ErrorMessage, @ErrorSeverity, @ErrorState, @ErrorLine, @ErrorProcedure);
else
raiserror(@ErrorMessage, @ErrorSeverity, @ErrorState);
return(3);
end
end catch
end
Use RETURN
inmediatamente después de RAISERROR()
y no ejecutará más el procedimiento.