Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Грешка при задействане:Текущата транзакция не може да бъде ангажирана и не може да поддържа операции, които записват в регистрационния файл

Тази грешка възниква, когато използвате блок 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)
    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

Когато четвъртото вмъкване причини грешка, пакетът се прекратява и транзакцията се връща обратно. Засега няма изненади.

Сега нека се опитаме да се справим с тази грешка с блок 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

Уловихме грешката с дублиращия се ключ, но иначе не сме по-добре. Нашата партида все още се прекратява и нашата транзакция все още се връща обратно. Причината всъщност е много проста:

Блоковете TRY/CATCH не засягат транзакциите.

Поради наличието на XACT_ABORT ON, в момента, в който възникне грешката на дублиращия се ключ, транзакцията е обречена. Това е направено за. Ранено е смъртоносно. Прострелян е в сърцето... и грешката е виновна. TRY/CATCH дава на SQL Server... лошо име. (съжалявам, не можах да устоя)

С други думи, няма да НИКОГА ангажират и ще ВИНАГИ бъде върнат назад. Всичко, което блокът TRY/CATCH може да направи, е да прекъсне падането на трупа. Можем да използваме XACT_STATE() функция, за да видим дали нашата транзакция е ангажирана. Ако не е, единствената опция е да отмените транзакцията.

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

Тригерите винаги се изпълняват в контекста на транзакция, така че ако можете да избегнете използването на TRY/CATCH в тях, нещата са много по-прости.

За решение на вашия проблем CLR Stored Proc може да се свърже обратно към SQL Server в отделна връзка, за да изпълни динамичния SQL. Получавате способността да изпълнявате кода в нова транзакция, а логиката за обработка на грешки е лесна за писане и лесна за разбиране в 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. Избягвайте да наименувате съхранените от потребителя процедури SP% или SP_%

  2. OpenRowSet и OpenDataSet без права на sysadmin

  3. Как да се свържете с няколко SQL сървъра с едно щракване (група регистрирани сървъри) - SQL Server / TSQL урок, част 5

  4. Връщане на списък с таблици и изгледи в SQL Server с помощта на T-SQL (sp_tables)

  5. SQL Server - намерете n-то срещане в низ