Jaka jest korzyść z używania „SET XACT_ABORT ON” w procedurze składowanej?

Odpowiedzi:

231

SET XACT_ABORT ONinstruuje SQL Server, aby wycofał całą transakcję i przerwał partię, gdy wystąpi błąd w czasie wykonywania. Obejmuje to przypadki takie jak przekroczenie limitu czasu polecenia występujące w aplikacji klienckiej, a nie w samym SQL Server (co nie jest objęte XACT_ABORT OFFustawieniem domyślnym ).

Ponieważ przekroczenie limitu czasu zapytania pozostawi transakcję otwartą, SET XACT_ABORT ONjest zalecane we wszystkich procedurach składowanych z jawnymi transakcjami (chyba że masz konkretny powód, aby zrobić inaczej), ponieważ konsekwencje wykonywania pracy przez aplikację na połączeniu z otwartą transakcją są katastrofalne.

Na blogu Dana Guzmana znajduje się naprawdę świetny przegląd ,

Ben Griswold
źródło
41
więc dlaczego nie jest włączona domyślnie?
Mike W.
1
Czy XACT_ABORT jest nadal wymagany, jeśli masz BEGIN TRY- BEGIN CATCHi ROLLBACKz BEGIN CATCHblokiem w Sql?
user20358
1
@ user20358 BEGIN TRY- BEGIN CATCHnie przechwytuje takich rzeczy, jak przekroczenie limitu czasu występującego w aplikacji klienckiej, a niektóre błędy SQL są również nie do wykrycia, pozostawiając otwartą transakcję, której się nie spodziewałeś.
Tom Lint,
37

Moim zdaniem SET XACT_ABORT ON stał się przestarzały przez dodanie BEGIN TRY / BEGIN CATCH w SQL 2k5. Przed blokami wyjątków w Transact-SQL obsługa błędów była naprawdę trudna, a niezrównoważone procedury były zbyt powszechne (procedury, które miały inny @@ TRANCOUNT na wyjściu w porównaniu z wejściem).

Dzięki dodaniu obsługi wyjątków języka Transact-SQL pisanie poprawnych procedur, które gwarantują prawidłowe zbilansowanie transakcji, jest znacznie łatwiejsze. Na przykład używam tego szablonu do obsługi wyjątków i transakcji zagnieżdżonych :

create procedure [usp_my_procedure_name]
as
begin
    set nocount on;
    declare @trancount int;
    set @trancount = @@trancount;
    begin try
        if @trancount = 0
            begin transaction
        else
            save transaction usp_my_procedure_name;

        -- Do the actual work here

lbexit:
        if @trancount = 0   
            commit;
    end try
    begin catch
        declare @error int, @message varchar(4000), @xstate int;
        select @error = ERROR_NUMBER(), @message = ERROR_MESSAGE(), @xstate = XACT_STATE();
        if @xstate = -1
            rollback;
        if @xstate = 1 and @trancount = 0
            rollback
        if @xstate = 1 and @trancount > 0
            rollback transaction usp_my_procedure_name;

        raiserror ('usp_my_procedure_name: %d: %s', 16, 1, @error, @message) ;
    end catch   
end
go

Pozwala mi na pisanie atomowych procedur, które wycofują tylko swoją własną pracę w przypadku naprawialnych błędów.

Jednym z głównych problemów, z jakimi borykają się procedury Transact-SQL, jest czystość danych : czasami otrzymane parametry lub dane w tabelach są po prostu błędne, co skutkuje zduplikowanymi błędami klucza, błędami ograniczeń referencyjnych, błędami ograniczeń sprawdzania i tak dalej. W końcu to właśnie rola tych ograniczeń, jeśli te błędy czystości danych byłyby niemożliwe i wszystkie zostałyby przechwycone przez logikę biznesową, wszystkie ograniczenia byłyby przestarzałe (dramatyczna przesada dodana dla efektu). Jeśli XACT_ABORT jest ON, wszystkie te błędy powodują utratę całej transakcji, w przeciwieństwie do możliwości kodowania bloków wyjątków, które z wdziękiem obsługują wyjątek. Typowym przykładem jest próba wykonania INSERT i powrót do UPDATE w przypadku naruszenia PK.

Remus Rusanu
źródło
9
Z wyjątkiem limitów czasu klienta ... i moim zdaniem SET XACT_ABORT jest bardziej efektywny w SQL 2005, ponieważ zachowanie jest bardziej przewidywalne: znacznie mniej błędów przerywania partii.
gbn
7
Zgadzam się w pewnym stopniu, ale planuję obsługę błędów wokół wszystkich ewentualności, ponieważ wiem, że jako administrator danych deweloperów będę winny, jeśli nastąpi przekroczenie limitu czasu polecenia.
gbn
4
@RemusRusanu Jak inaczej poradziłbyś sobie z długotrwałą, synchroniczną operacją na bazie danych?
Ian Boyd
5
Dokumentacja MSDN stwierdza: „XACT_ABORT musi być ustawiony na ON dla instrukcji modyfikacji danych w niejawnej lub jawnej transakcji dla większości dostawców OLE DB, w tym SQL Server. Jedynym przypadkiem, w którym ta opcja nie jest wymagana, jest sytuacja, w której dostawca obsługuje transakcje zagnieżdżone.” msdn.microsoft.com/en-us/library/ms188792(v=sql.120).aspx
Nathan
4
„Moim zdaniem SET XACT_ABORT ON stał się przestarzały przez dodanie BEGIN TRY / BEGIN CATCH” - słyszę cię, ale zobacz sommarskog.se/error_handling/Part1.html
Reversed Engineer
22

Cytując MSDN :

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. Gdy opcja SET XACT_ABORT jest wyłączona, w niektórych przypadkach tylko instrukcja języka Transact-SQL, która wywołała błąd, jest wycofywana, a transakcja kontynuuje przetwarzanie.

W praktyce oznacza to, że niektóre instrukcje mogą się nie powieść, pozostawiając transakcję „częściowo zakończoną” i wywołującemu może nie być żadnego znaku tego niepowodzenia.

Prosty przykład:

INSERT INTO t1 VALUES (1/0)    
INSERT INTO t2 VALUES (1/1)    
SELECT 'Everything is fine'

Ten kod zostałby wykonany „pomyślnie” przy wyłączonym XACT_ABORT i zakończyłby się błędem przy włączonym XACT_ABORT („INSERT INTO t2” nie zostanie wykonany, a aplikacja kliencka zgłosi wyjątek).

Bardziej elastycznym podejściem jest sprawdzenie @@ ERROR po każdej instrukcji (stara szkoła) lub użycie bloków TRY ... CATCH (MSSQL2005 +). Osobiście wolę ustawić XACT_ABORT ON, gdy nie ma powodu do zaawansowanej obsługi błędów.

VladV
źródło
8

Jeśli chodzi o timeouty klientów i użycie XACT_ABORT do ich obsługi, moim zdaniem istnieje co najmniej jeden bardzo dobry powód, aby mieć limity czasu w API klienta, takie jak SqlClient, a mianowicie chronić kod aplikacji klienta przed zakleszczeniami występującymi w kodzie serwera SQL. W tym przypadku kod klienta nie ma błędu, ale musi chronić go przed zablokowaniem na zawsze, czekając na zakończenie polecenia na serwerze. I odwrotnie, jeśli muszą istnieć limity czasu klienta, aby chronić kod klienta, XACT_ABORT ON musi chronić kod serwera przed przerwaniami klienta, na wypadek gdyby kod serwera trwał dłużej, niż klient jest skłonny czekać.

ionutm
źródło
1

Jest używany w zarządzaniu transakcjami, aby zapewnić, że wszelkie błędy spowodują wycofanie transakcji.

Dan Diplo
źródło