sql >> Databasteknik >  >> RDS >> Sqlserver

Trigger Error:Den aktuella transaktionen kan inte utföras och kan inte stödja operationer som skriver till loggfilen

Det här felet uppstår när du använder ett försök/fångstblock i en transaktion. Låt oss överväga ett trivialt exempel:

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 fjärde infogningen orsakar ett fel, avslutas batchen och transaktionen rullar tillbaka. Inga överraskningar än så länge.

Låt oss nu försöka hantera det felet med ett TRY/CATCH-block:

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 fångade dubblettnyckelfelet, men annars har vi det inte bättre. Vår batch avslutas fortfarande och vår transaktion återställs fortfarande. Anledningen är faktiskt väldigt enkel:

TRY/CATCH-blockeringar påverkar inte transaktioner.

På grund av att ha XACT_ABORT PÅ, i det ögonblick då dubblettnyckelfelet inträffar, är transaktionen dömd. Det är gjort för. Den har blivit dödligt skadad. Det har skjutits genom hjärtat... och felet är att skylla på. TRY/CATCH ger SQL Server...ett dåligt namn. (förlåt, kunde inte låta bli)

Med andra ord, det kommer ALDRIG begå och kommer ALLTID rullas tillbaka. Allt ett TRY/CATCH-block kan göra är att bryta likets fall. Vi kan använda XACT_STATE() funktion för att se om vår transaktion är genomförbar. Om det inte är det är det enda alternativet att återställa transaktionen.

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

Triggers körs alltid inom ramen för en transaktion, så om du kan undvika att använda TRY/CATCH inuti dem är saker mycket enklare.

För en lösning på ditt problem kan en CLR Stored Proc ansluta tillbaka till SQL Server i en separat anslutning för att exekvera den dynamiska SQL-en. Du får möjligheten att exekvera koden i en ny transaktion och felhanteringslogiken är både lätt att skriva och lätt att förstå i C#.




  1. pgAdmin 4 ver. 1.5 - inga felmeddelanden

  2. Heltal utanför intervallet på Postgres DB

  3. Hur väljer man flera rader med deras sammansatta primärnycklar i JOOQ?

  4. finns det något sätt att logga alla misslyckade SQL-satser i Oracle 10g