2008-09-16 3 views
77

Ich wurde gerade von etwas in TSQL überrascht. Ich dachte, wenn XACT_ABORT auf ist, so etwas wieWarum läuft Sql Server nach raiserror weiter, wenn xact_abort an ist?

Aufruf
raiserror('Something bad happened', 16, 1); 

stoppen würde die Ausführung der gespeicherten Prozedur (oder jede Charge).

Aber meine ADO.NET-Fehlermeldung hat gerade das Gegenteil bewiesen. Ich habe sowohl die Fehlermeldung raiserror in der Ausnahmebedingungsnachricht als auch das nächste, was danach abgebrochen wurde.

Dies ist meine Abhilfe (die ohnehin meine Gewohnheit ist), aber es scheint nicht, wie es notwendig sein sollte:

if @somethingBadHappened 
    begin; 
     raiserror('Something bad happened', 16, 1); 
     return; 
    end; 

Die docs sagen:

Wenn SET XACT_ABORT ist Wenn eine Transact-SQL-Anweisung einen Laufzeitfehler auslöst, wird die gesamte Transaktion beendet und zurückgesetzt.

Bedeutet das, dass ich eine explizite Transaktion verwenden muss?

+0

Nur getestet und "RAISERROR" wird tatsächlich die Ausführung beenden, wenn der Schweregrad auf 17 oder 18 statt 16 gesetzt ist. – reformed

Antwort

43

Dies ist By Design TM, wie Sie auf Connect vom SQL Server-Team der Antwort auf eine ähnliche Frage sehen:

Vielen Dank für Ihr Feedback. Die XACT_ABORT-Set-Option hat keine Auswirkung auf das Verhalten der RAISERROR-Anweisung. Wir berücksichtigen Ihr Feedback, um dieses Verhalten für eine zukünftige Version von SQL Server zu ändern.

Ja, das ist ein bisschen ein Problem für einige, die RAISERROR mit hohem Schweregrad gehofft (wie 16) würde das gleiche wie ein SQL-Ausführungsfehler - es ist nicht.

Ihre Problemumgehung betrifft nur das, was Sie tun müssen, und die Verwendung einer expliziten Transaktion hat keine Auswirkungen auf das Verhalten, das Sie ändern möchten.

+0

Danke Philip. Der Link, auf den Sie verwiesen haben, scheint nicht verfügbar zu sein. –

+2

Link funktioniert gut, wenn Sie jemals danach suchen müssen, Titel "Habe RAISERROR mit XACT_ABORT arbeiten", Autor "jorundur", ID: 275308 – JohnC

22

Wenn Sie einen try/catch-Block verwenden, führt eine raiserror-Fehlernummer mit dem Schweregrad 11-19 dazu, dass die Ausführung zum catch-Block springt.

Jeder Schweregrad über 16 ist ein Systemfehler. Um den folgenden Code zu demonstrieren, wird ein try/catch-Block eingerichtet und eine gespeicherte Prozedur ausgeführt, von der wir annehmen, dass sie fehlschlägt:

Wir nehmen an, dass wir eine Tabelle [dbo] haben. dbo]. [AssumeThisFails], die fehlschlagen, wenn wir es

-- 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 
19

Verwenden RETURN unmittelbar nach RAISERROR() ausführen und es wird das Verfahren nicht weiter ausführen.

+8

Sie können 'Rollback-Transaktion' aufrufen, bevor Sie 'return' aufrufen. –

+1

Wahrscheinlich müssen Sie etwas in Ihrem catch-Block tun – sqluser

12

Wie auf MSDN hingewiesen, sollte die THROW Anweisung anstelle von RAISERROR verwendet werden.

Die beiden verhalten sich slightly differently. Wenn jedoch XACT_ABORT auf ON gesetzt ist, sollten Sie immer den Befehl THROW verwenden.

+24

Wenn Sie nicht 2k12 (oder höher, wenn es herauskommt), dann gibt es keine THROW-Anweisung zu haben. –

Verwandte Themen