sql >> Database teknologi >  >> RDS >> Sqlserver

Triggerfejl:Den aktuelle transaktion kan ikke forpligtes og kan ikke understøtte operationer, der skriver til logfilen

Denne fejl opstår, når du bruger en try/catch-blok inde i en transaktion. Lad os overveje et trivielt eksempel:

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

Når den fjerde indsættelse forårsager en fejl, afsluttes batchen, og transaktionen ruller tilbage. Ingen overraskelser indtil videre.

Lad os nu forsøge at håndtere den fejl med en TRY/CATCH-blok:

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

Vi fangede dubletnøglefejlen, men ellers er vi ikke bedre stillet. Vores batch bliver stadig afsluttet, og vores transaktion bliver stadig rullet tilbage. Årsagen er faktisk meget enkel:

TRY/CATCH-blokeringer påvirker ikke transaktioner.

På grund af at have XACT_ABORT TIL, i det øjeblik dubletnøglefejlen opstår, er transaktionen dømt. Det er gjort for. Den er blevet dødeligt såret. Det er blevet skudt gennem hjertet... og fejlen er skyld. TRY/CATCH giver SQL Server...et dårligt navn. (undskyld, kunne ikke lade være)

Med andre ord vil det ALDRIG begå og vil ALTID blive rullet tilbage. Alt en TRY/CATCH-blok kan gøre er at bryde ligets fald. Vi kan bruge XACT_STATE() funktion for at se, om vores transaktion er forpligtende. Hvis det ikke er det, er den eneste mulighed at rulle transaktionen tilbage.

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

Triggere udføres altid i forbindelse med en transaktion, så hvis du kan undgå at bruge TRY/CATCH inde i dem, er tingene meget enklere.

For en løsning på dit problem kunne en CLR Stored Proc oprette forbindelse tilbage til SQL Server i en separat forbindelse for at udføre den dynamiske SQL. Du får muligheden for at udføre koden i en ny transaktion, og fejlhåndteringslogikken er både nem at skrive og let at forstå i C#.




  1. Rails:visning af et billede fra et klatfelt i en database

  2. MySQL-fejl Der kan kun være én TIMESTAMP-kolonne med CURRENT_TIMESTAMP i DEFAULT-sætning, selvom jeg ikke gør noget forkert

  3. Hvordan fungerer ANSI_NULLS i TSQL?

  4. Sådan fungerer SQLite Length()