sql >> Database >  >> RDS >> Sqlserver

Triggerfout:de huidige transactie kan niet worden vastgelegd en ondersteunt geen bewerkingen die naar het logbestand schrijven

Deze fout treedt op wanneer u een try/catch-blok in een transactie gebruikt. Laten we een triviaal voorbeeld bekijken:

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

Wanneer de vierde insert een fout veroorzaakt, wordt de batch beëindigd en wordt de transactie teruggedraaid. Tot nu toe geen verrassingen.

Laten we nu proberen die fout af te handelen met een 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

We hebben de dubbele sleutelfout ontdekt, maar verder zijn we niet beter af. Onze batch wordt nog steeds beëindigd en onze transactie wordt nog steeds teruggedraaid. De reden is eigenlijk heel simpel:

TRY/CATCH-blokkeringen hebben geen invloed op transacties.

Omdat XACT_ABORT AAN staat, is de transactie gedoemd op het moment dat de dubbele sleutelfout optreedt. Het is gedaan voor. Het is dodelijk gewond. Het is door het hart geschoten... en de fout is de schuld. TRY/CATCH geeft SQL Server... een slechte naam. (sorry, kon het niet laten)

Met andere woorden, het zal NOOIT zich committeren en zal ALTIJD teruggedraaid worden. Het enige dat een TRY/CATCH-blok kan doen, is de val van het lijk breken. We kunnen de XACT_STATE() . gebruiken functie om te zien of onze transactie begaanbaar is. Als dit niet het geval is, is de enige optie om de transactie terug te draaien.

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 worden altijd uitgevoerd binnen de context van een transactie, dus als u het gebruik van TRY/CATCH erin kunt vermijden, is het veel eenvoudiger.

Voor een oplossing voor uw probleem kan een CLR Stored Proc in een aparte verbinding terug verbinding maken met SQL Server om de dynamische SQL uit te voeren. U krijgt de mogelijkheid om de code in een nieuwe transactie uit te voeren en de logica voor foutafhandeling is zowel gemakkelijk te schrijven als gemakkelijk te begrijpen in C#.




  1. Django- en mysql-containers koppelen met docker-compose

  2. Relationeel databaseschema voor eventsourcing

  3. selecteer het laatste bericht van het gesprek voor elk gesprek in mysql

  4. Zijn meerdere externe sleutels in één veld mogelijk?