web-dev-qa-db-ja.com

トリガーがテーブルに適用された後のコミットできないT-SQLトランザクション

おはようございます。入力した値(長さ、幅、高さ)を評価し、それらの入力した値に基づいて、同じレコードの列(タイプ)を対応する値で更新するトリガーをテーブルに作成しました。トリガーを適用した後、すべてが完全に機能しますが、特定の時間に次のエラーが発生します。

予期しないエラー:現在のトランザクションはコミットできず、ログファイルに書き込む操作をサポートできません。トランザクションをロールバックします。ステートメントは終了されました

XACT_ABORT()およびXACT_STATE()でTry/Catchブロックを使用してエラーを処理するスレッドを見つけましたが、それでも同じ問題が発生しています。繰り返しますが、この問題は常に発生しているわけではなく、時々発生しているだけです。これが私のトリガーです:

CREATE TRIGGER [dbo].[UTRAfterUpdate] 
   ON  [dbo].[TBL_STOCK] 
   AFTER UPDATE
AS 
BEGIN

    SET NOCOUNT ON;


    DECLARE @CBM FLOAT, @T NVARCHAR(1);
    DECLARE C CURSOR FOR SELECT LEG_PRODUCT_ID, PR_LENGTH1, 
                         PR_WIDTH1, PR_HEIGHT1, PR_TYPE FROM INSERTED
    DECLARE @ID BIGINT, @L INT, @W INT, @H INT, @TYPE NVARCHAR(1)

    SET XACT_ABORT ON;
    BEGIN TRY
        BEGIN TRANSACTION;
        OPEN C
        FETCH NEXT FROM C INTO @ID, @L, @W, @H, @TYPE
        WHILE @@FETCH_STATUS = 0
        BEGIN
            IF(UPDATE(PR_LENGTH1) OR UPDATE (PR_WIDTH1) OR UPDATE(PR_HEIGHT1))
            BEGIN
                SET @CBM = (cast(@l AS DECIMAL(18,2))/100
                                       *cast(@w AS DECIMAL(18,2))/100
                                       *cast(@h AS DECIMAL(18,2))/100)
                SET @T = CASE 
                    WHEN (@CBM > 0.00  AND @CBM <= 13.00) 
                          AND (@h <= 170) THEN 'C'
                    WHEN (@CBM > 0.00  AND @CBM <= 13.00)
                          AND (@h >  170 AND @h <= 220) THEN 'V'                       
                    WHEN (@CBM > 13.00 AND @CBM <= 20.00)
                          AND (@h <= 220) THEN 'V'
                    WHEN (@CBM > 0.00  AND @CBM <= 20.00)
                          AND (@h >  220 AND @h <= 250) THEN 'B'                         
                    WHEN (@CBM > 20.00 AND @CBM <= 30.00)
                          AND (@h <= 250) THEN 'B' 
                    WHEN (@CBM > 0.00  AND @CBM <= 30.00)
                          AND (@h >  250) THEN 'T'
                    WHEN (@CBM > 30.00) THEN 'T'
                    ELSE @type
                END
                IF(@type <> @T)
                    UPDATE dbo.TBL_STOCK
                                    SET PR_TYPE = @t,
                                    LAST_ACTION = 'UTR',
                                    LAST_USER = 'System' 
                    FROM TBL_STOCK STOCK INNER JOIN INSERTED INS
                            ON STOCK.LEG_PRODUCT_ID = INS.LEG_PRODUCT_ID
                    WHERE STOCK.LEG_PRODUCT_ID = @id
            END
        FETCH NEXT FROM C INTO @id, @l, @w, @h, @type    
        END
        COMMIT TRANSACTION;
    END TRY
    BEGIN CATCH
        IF (XACT_STATE()) = -1
        BEGIN
            PRINT 'The transaction is in an uncommittable state.'
            + ' Rolling back transaction.'
            ROLLBACK TRANSACTION;
        END;
        IF (XACT_STATE()) = 1
        BEGIN
            PRINT 'The transaction is committable.'
            + ' Committing transaction.'
            COMMIT TRANSACTION;   
        END;
    END CATCH

CLOSE C
DEALLOCATE C
END

問題を解決するのを手伝ってくれませんか?

前もって感謝します!

1

トリガーに1行だけが渡されるとは絶対に言いません。トリガーはすべての行を処理し、単一のステートメントで更新されます。 (ジョン・セイゲルも同じことを言っているようです。)

問題は、トリガー内でエラーが発生し、トランザクションをコミットできない状態になることです。主な問題は、トランザクションがコミットできなくなった理由を特定することです。

http://msdn.Microsoft.com/en-us/library/ms179296(v = sql.105).aspx を参照してください

一部にはそれは言う:

TRY…CATCH構文内では、トランザクションは、トランザクションが開いたままであるがコミットできない状態になる可能性があります。トランザクションは、データの変更やセーブポイントへのロールバックの試行など、トランザクションログへの書き込みを生成するアクションを実行できません。ただし、この状態では、トランザクションによって取得されたロックは維持され、接続も開いたままになります。トランザクションの影響は元に戻されませんROLLBACKステートメントが発行されるまで、またはバッチが終了してトランザクションがデータベースエンジンによって自動的にロールバックされるまで。トランザクションがコミットできない状態になったときにエラーメッセージが送信されなかった場合、バッチが完了すると、コミットできないトランザクションが検出されてロールバックされたことを示すエラーメッセージがクライアントアプリケーションに送信されます。

したがって、TRY/CATCHとXACT_STATEに対するテストは正しいです。 「ドゥームされた」トランザクションを生成するためにすでに起こったことを克服するには遅すぎます。

Balmukundは、このトピックについて以下のブログ投稿を公開しています。彼は、破滅したトランザクションを引き起こす可能性のあるすべての条件を定義することはできないと指摘していますが、いくつかのヒントを提供しています:

http://blogs.msdn.com/b/sqlserverfaq/archive/2011/05/11/errors-raised-with-severity-level-16-may-cause-transactions-into-doomed-state。 aspx

私たちの集中的な調査によると、XACT_STATEの変更は単一のルールでは管理されず、トランザクションの状態(ユーザーまたはシステムが開始したか、アクティブなトランザクションかどうか)と発生するエラーの種類に依存すると断言しました。たとえば、トランザクションでCONVERT\CASTエラーが発生し、障害時にDDL操作(ALTER TABLE\DATABASE\INDEXなど)が発生すると、トランザクションの状態が-1になり、トランザクションが終了します。

トランザクションを悲惨な状態にする可能性があるもう1つの例は、ターゲットのデータ型と互換性のない値を変換しようとした場合です。

3
RLF