Me sorprendió algo en TSQL. Pensé que si xact_abort estaba activado, llamar a algo como
raiserror('Something bad happened', 16, 1);
detendría la ejecución del procedimiento almacenado (o cualquier lote).
Pero mi mensaje de error de ADO.NET demostró lo contrario. Recibí tanto el mensaje de error raiserror en el mensaje de excepción, más lo siguiente que se rompió después de eso.
Esta es mi solución alternativa (que es mi hábito de todos modos), pero no parece que deba ser necesaria:
if @somethingBadHappened
begin;
raiserror('Something bad happened', 16, 1);
return;
end;
Los doctores dicen esto:
Cuando SET XACT_ABORT está en ON, si una instrucción Transact-SQL genera un error en tiempo de ejecución, toda la transacción se termina y se revierte.
¿Eso significa que debo utilizar una transacción explícita?
sql
sql-server
tsql
Eric Z Barba
fuente
fuente
RAISERROR
, de hecho, finalizará la ejecución si la gravedad se establece en 17 o 18, en lugar de 16.RAISERROR
, de hecho, no finalizará la ejecución si la gravedad se establece en 17 o 18, en lugar de 16.Messages
pestaña que no verá(X rows affected)
oPRINT
mensajes, que yo diría que es una completa mentira !Respuestas:
Esto es By Design TM , como puede ver en Connect por la respuesta del equipo de SQL Server a una pregunta similar:
Sí, esto es un problema para algunos que esperaban que
RAISERROR
con una gravedad alta (como16
) fuera lo mismo que un error de ejecución de SQL, no lo es.Su solución es solo lo que necesita hacer, y el uso de una transacción explícita no tiene ningún efecto en el comportamiento que desea cambiar.
fuente
Si usa un bloque try / catch, un número de error raiserror con gravedad 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 configura un bloque try / catch y ejecuta un procedimiento almacenado que asumimos 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
fuente
Úselo
RETURN
inmediatamente despuésRAISERROR()
y no ejecutará más el procedimiento.fuente
rollback transaction
antes de llamarreturn
.Como se indica en los documentos
SET XACT_ABORT
, laTHROW
declaración debe usarse en lugar deRAISERROR
.Los dos se comportan de manera ligeramente diferente . Pero cuando
XACT_ABORT
se establece en ON, siempre debe usar elTHROW
comando.fuente