2011-09-20 3 views
26

Mam podobny problem do The current transaction cannot be committed and cannot support operations that write to the log file, ale mam kolejne pytanie.Błąd transakcji SQL: Bieżąca transakcja nie może zostać zatwierdzona i nie obsługuje operacji zapisujących do pliku dziennika.

Odpowiedź nie odwołuje Using TRY...CATCH in Transact-SQL, który Wrócę w sekundę ...

Mój kod (dziedziczone, oczywiście) ma uproszczoną postać:

SET NOCOUNT ON 
SET XACT_ABORT ON 

CREATE TABLE #tmp 

SET @transaction = 'insert_backtest_results' 
BEGIN TRANSACTION @transaction 

BEGIN TRY 

    --do some bulk insert stuff into #tmp 

END TRY 

BEGIN CATCH 
    ROLLBACK TRANSACTION @transaction 
    SET @errorMessage = 'bulk insert error importing results for backtest ' 
     + CAST(@backtest_id as VARCHAR) + 
     '; check backtestfiles$ directory for error files ' + 
     ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
     ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) + 
     ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) + 
     ' error_state ' + CAST(ERROR_STATE() AS VARCHAR) + 
     ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR) 
    RAISERROR(@errorMessage, 16, 1) 
    RETURN -666 
END CATCH 

BEGIN TRY 

    EXEC usp_other_stuff_1 @whatever 

    EXEC usp_other_stuff_2 @whatever 

    -- a LOT of "normal" logic here... inserts, updates, etc... 

END TRY 

BEGIN CATCH 

    ROLLBACK TRANSACTION @transaction 
    SET @errorMessage = 'error importing results for backtest ' 
     + CAST(@backtest_id as VARCHAR) + 
     ' error_number: ' + CAST(ERROR_NUMBER() AS VARCHAR) + 
     ' error_message: ' + CAST(ERROR_MESSAGE() AS VARCHAR(200)) + 
     ' error_severity: ' + CAST(ERROR_SEVERITY() AS VARCHAR) + 
     ' error_state ' + CAST(ERROR_STATE() AS VARCHAR) + 
     ' error_line: ' + CAST(ERROR_LINE() AS VARCHAR) 
    RAISERROR(@errorMessage, 16, 1) 
    RETURN -777 

END CATCH 

RETURN 0 

myślę Mam wystarczającą ilość informacji, aby po prostu się z tym bawić i sam to rozgryźć ... niestety odtworzenie błędu okazuje się prawie niemożliwe. Mam więc nadzieję, że zapytanie tutaj pomoże wyjaśnić moją wiedzę na temat problemu i rozwiązania.

Ta procedura przechowywana jest przerywany, rzucanie błędy jak ten:

error importing results for backtest 9649 error_number: 3930 error_message: The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction. error_severity: 16 error_state 1 error_line: 217

Tak oczywiście błąd jest pochodzących od 2 bloku catch

Na podstawie tego co czytałem w Using TRY...CATCH in Transact-SQL, ja pomyśl, co się dzieje, gdy wyjątek zostanie rzucony, użycie XACT_ABORT powoduje, że transakcja zostaje "zakończona i wycofana" ... a następnie pierwsza linia BEGIN CATCH ślepo próbuje wycofać.

nie wiem dlaczego oryginalny deweloper włączona XACT_ABORT, więc myślę, że lepszym rozwiązaniem (niż usunięcie go) byłoby użyć XACT_STATE() tylko cofnąć jeśli nie jest transakcją (<>0). Czy to brzmi rozsądnie? Czy czegoś brakuje?

Dodaje się również wzmianka o logowaniu się do komunikatu o błędzie: Czy istnieje inny problem, potencjalnie związany z konfiguracją? Czy korzystanie z tego RAISEERROR() w tym scenariuszu przyczynia się do problemu? Czy to się loguje, w jakimś przypadku, gdy logowanie nie jest możliwe, o czym wspomina komunikat o błędzie?

Odpowiedz

27

Należy zawsze sprawdzić pod kątem XACT_STATE(), nieistotny dla ustawienia XACT_ABORT. Mam przykład szablon dla procedur przechowywanych, które muszą obsługiwać transakcje w ramach TRY/CATCH w Exception handling and nested transactions:

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 
+0

Twój szablon przyjmuje transakcje wewnątrz bloku try; mamy wiele bloków try wewnątrz 1 transakcji. –

+0

@Adam: chodzi o sposób obsługi "XACT_STATE" i transakcji w bloku CATCH. Możesz mieć wiele bloków try w jednej transakcji przy użyciu tego samego szablonu. Chodzi o to, aby zrozumieć, w jaki sposób transakcje i bloki catch wchodzą ze sobą w interakcje, a jako bonus można uzyskać obsługę transakcji zagnieżdżonych i punktów zapisywania, co jest bardzo przydatne w przetwarzaniu wsadowym, ponieważ daje możliwość wznowienia reszty partii, nawet jeśli wpis nie powiódł się. –

+0

Poszedłem do przodu i zawijałem instrukcję rollback w 'if XACT_STATE() <> 0', ale tylko czas pokaże, czy to rozwiązało dla nas. Chyba pójdę do przodu i zaakceptuję na razie twoją odpowiedź. –

12

Istnieje kilka nieporozumień w powyższej dyskusji.

Po pierwsze, zawsze możesz ODWOŁAĆ transakcję ... bez względu na stan transakcji. Musisz więc tylko sprawdzić XACT_STATE przed COMMIT, a nie przed wycofaniem.

Jeśli chodzi o błąd w kodzie, będziesz chciał umieścić transakcję wewnątrz TRY. Następnie w łapać, pierwszą rzeczą, jaką należy zrobić, jest następujący:

IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION @transaction 

następnie, po powyższym zestawieniu, a następnie można wysłać e-mail lub cokolwiek jest potrzebne. (FYI: Jeśli wyślesz wiadomość e-mail PRZED wycofaniem, to na pewno otrzymasz komunikat "nie można ... zapisać do pliku dziennika").

Ten numer pochodzi z zeszłego roku, więc mam nadzieję, że rozwiązałeś to przez teraz :-) Remus wskazał ci właściwy kierunek.

Zgodnie z regułą ... TRY natychmiast przejdzie do CATCH, gdy wystąpi błąd. Następnie, gdy jesteś w POŁOWIE, możesz użyć XACT_STATE, aby zdecydować, czy możesz zatwierdzić. Ale jeśli zawsze chcesz ODWOŁAĆ się w haczyku, to nie musisz w ogóle sprawdzać stanu.