Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Błąd wyzwalacza:bieżąca transakcja nie może zostać zatwierdzona i nie może obsługiwać operacji zapisu do pliku dziennika

Ten błąd występuje, gdy używasz bloku try/catch w transakcji. Rozważmy trywialny przykład:

SET XACT_ABORT ON

IF object_id('tempdb..#t') IS NOT NULL
    DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)

BEGIN TRAN
    INSERT INTO #t (i) VALUES (1)
    INSERT INTO #t (i) VALUES (2)
    INSERT INTO #t (i) VALUES (3)
    INSERT INTO #t (i) VALUES (1) -- dup key error, XACT_ABORT kills the batch
    INSERT INTO #t (i) VALUES (4) 

COMMIT  TRAN
SELECT * FROM #t

Gdy czwarta wstawka powoduje błąd, partia jest przerywana i transakcja jest wycofywana. Na razie żadnych niespodzianek.

Teraz spróbujmy obsłużyć ten błąd za pomocą bloku TRY/CATCH:

SET XACT_ABORT ON
IF object_id('tempdb..#t') IS NOT NULL
    DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)

BEGIN TRAN
    INSERT INTO #t (i) VALUES (1)
    INSERT INTO #t (i) VALUES (2)
    BEGIN TRY
        INSERT INTO #t (i) VALUES (3)
        INSERT INTO #t (i) VALUES (1) -- dup key error
    END TRY
    BEGIN CATCH
        SELECT ERROR_MESSAGE()
    END CATCH  
    INSERT INTO #t (i) VALUES (4)
    /* Error the Current Transaction cannot be committed and 
    cannot support operations that write to the log file. Roll back the transaction. */

COMMIT TRAN
SELECT * FROM #t

Wykryliśmy błąd zduplikowanego klucza, ale poza tym nie jesteśmy w lepszej sytuacji. Nasza partia nadal jest przerywana, a nasza transakcja nadal jest wycofywana. Powód jest bardzo prosty:

Bloki TRY/CATCH nie wpływają na transakcje.

Ze względu na to, że XACT_ABORT ON, w momencie wystąpienia błędu zduplikowanego klucza, transakcja jest stracona. To jest zrobione. Został śmiertelnie ranny. Został przestrzelony w serce... i winę za ten błąd. TRY/CATCH nadaje SQL Serverowi... złą nazwę. (przepraszam, nie mogłem się oprzeć)

Innymi słowy, NIGDY zobowiąże się i ZAWSZE wycofać. Jedyne, co może zrobić blok TRY/CATCH, to powstrzymać upadek trupa. Możemy użyć XACT_STATE() funkcja, aby sprawdzić, czy nasza transakcja jest zatwierdzalna. Jeśli tak nie jest, jedyną opcją jest wycofanie transakcji.

SET XACT_ABORT ON -- Try with it OFF as well.
IF object_id('tempdb..#t') IS NOT NULL
    DROP TABLE #t
CREATE TABLE #t (i INT NOT NULL PRIMARY KEY)

BEGIN TRAN
    INSERT INTO #t (i) VALUES (1)
    INSERT INTO #t (i) VALUES (2)

    SAVE TRANSACTION Save1
    BEGIN TRY
        INSERT INTO #t (i) VALUES (3)
        INSERT INTO #t (i) VALUES (1) -- dup key error
    END TRY
    BEGIN CATCH
        SELECT ERROR_MESSAGE()
        IF XACT_STATE() = -1 -- Transaction is doomed, Rollback everything.
            ROLLBACK TRAN
        IF XACT_STATE() = 1 --Transaction is commitable, we can rollback to a save point
            ROLLBACK TRAN Save1
    END CATCH  
    INSERT INTO #t (i) VALUES (4)

IF @@TRANCOUNT > 0
    COMMIT TRAN
SELECT * FROM #t

Wyzwalacze zawsze działają w kontekście transakcji, więc jeśli możesz uniknąć używania w nich TRY/CATCH, wszystko jest znacznie prostsze.

Aby rozwiązać ten problem, CLR Stored Proc może połączyć się ponownie z SQL Server w oddzielnym połączeniu, aby wykonać dynamiczny SQL. Zyskujesz możliwość wykonania kodu w nowej transakcji, a logika obsługi błędów jest zarówno łatwa do napisania, jak i zrozumiała w C#.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak naprawić „Tylko jedno wyrażenie można określić na liście wyboru…” w SQL Server

  2. varchar(max) mapowanie typu danych odbc do ms-access2003

  3. Rozmiar typu danych zmiennej w sql

  4. Łatwy sposób wybrania odpowiedniej kolumny jako klucza podstawowego dla danej tabeli

  5. Pule połączeń w .NET/SQL Server?