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.Messagespestaña que no verá(X rows affected)oPRINTmensajes, 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
RAISERRORcon 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 endfuente
Úselo
RETURNinmediatamente despuésRAISERROR()y no ejecutará más el procedimiento.fuente
rollback transactionantes de llamarreturn.Como se indica en los documentos
SET XACT_ABORT, laTHROWdeclaración debe usarse en lugar deRAISERROR.Los dos se comportan de manera ligeramente diferente . Pero cuando
XACT_ABORTse establece en ON, siempre debe usar elTHROWcomando.fuente