Właśnie coś mnie zaskoczyło w TSQL. Pomyślałem, że jeśli xact_abort jest włączony, dzwonię do czegoś takiego
raiserror('Something bad happened', 16, 1);
zatrzymałoby wykonywanie procedury składowanej (lub dowolnej partii).
Ale mój komunikat o błędzie ADO.NET właśnie pokazał coś przeciwnego. Otrzymałem zarówno komunikat o błędzie raiserror w komunikacie o wyjątku, jak i następną rzecz, która się zepsuła.
To jest moje obejście (które i tak jest moim nawykiem), ale nie wydaje się, aby było to konieczne:
if @somethingBadHappened
begin;
raiserror('Something bad happened', 16, 1);
return;
end;
Doktorzy mówią tak:
Gdy SET XACT_ABORT jest ON, jeśli instrukcja Transact-SQL spowoduje błąd w czasie wykonywania, cała transakcja zostanie zakończona i wycofana.
Czy to oznacza, że muszę używać jawnej transakcji?
sql
sql-server
tsql
Eric Z Beard
źródło
źródło
RAISERROR
faktycznie zakończy wykonywanie, jeśli poziom ważności jest ustawiony na 17 lub 18 zamiast 16.RAISERROR
faktycznie nie przerywa wykonywania, jeśli ważność jest ustawiona na 17 lub 18 zamiast 16.Messages
zakładkę, zobaczysz brak(X rows affected)
lubPRINT
komunikaty, co powiedziałbym, że jest kompletnym kłamstwem !Odpowiedzi:
To jest By Design TM , jak można zobaczyć w Connect, w odpowiedzi zespołu SQL Server na podobne pytanie:
Tak, jest to trochę problem dla niektórych, którzy mieli nadzieję, że
RAISERROR
wysoka waga (podobna16
) będzie taka sama jak błąd wykonania SQL - tak nie jest.Twoje obejście dotyczy tylko tego, co musisz zrobić, a użycie jawnej transakcji nie ma żadnego wpływu na zachowanie, które chcesz zmienić.
źródło
Jeśli używasz bloku try / catch, numer błędu raiserror o wadze 11-19 spowoduje, że wykonanie przeskoczy do bloku catch.
Każdy poziom istotności powyżej 16 jest błędem systemu. Aby zademonstrować następujący kod, ustaw blok try / catch i wykonuje procedurę składowaną, która, jak zakładamy, zakończy się niepowodzeniem:
załóżmy, że mamy tabelę [dbo]. [Błędy] do przechowywania błędów zakładamy, że mamy procedurę składowaną [dbo]. [AssumeThisFails], która zakończy się niepowodzeniem, gdy ją wykonamy
-- 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
źródło
Użyj
RETURN
natychmiast po,RAISERROR()
a procedura nie będzie dalej wykonywana.źródło
rollback transaction
przed rozmowąreturn
.Jak wskazano w dokumentach dla
SET XACT_ABORT
,THROW
należy użyć oświadczenia zamiastRAISERROR
.Obaj zachowują się nieco inaczej . Ale kiedy
XACT_ABORT
jest ustawiony na ON, zawsze powinieneś używać tegoTHROW
polecenia.źródło