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
    

    4 回目の挿入でエラーが発生すると、バッチは終了し、トランザクションはロールバックします。これまでのところ驚くことはありません。

    それでは、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. Oracle NetSuite Advanced PDFテンプレートにはGroupby関数とSUM関数がありますか?

    2. タイプから一時テーブルを作成するにはどうすればよいですか?

    3. ウィンドウ化された集計関数の論理読み取りが非常に高いのはなぜですか?

    4. Mysqlはキーの2つのテーブルを結合します