web-dev-qa-db-ja.com

TSQL:トリガーでのTry-Catchトランザクション

Microsoft Server 2005を使用して、トリガー内にtry-catchステートメントを配置しようとしています。

BEGIN TRANSACTION
BEGIN TRY
    --Some More SQL
    COMMIT TRANSACTION
END TRY
BEGIN CATCH
    IF (XACT_STATE()) = -1
    BEGIN
        ROLLBACK TRANSACTION;
    END;
END CATCH

問題は、try-catchブロックによって何かがキャッチされた場合にトリガーが失敗することを望まないことです。現時点では、「トランザクションはトリガーで終了しました。バッチは中止されました。」というエラーが表示されます。トランザクションが失敗した場合。トリガーを正常に失敗させるにはどうすればよいですか?


さらに、トランザクションを削除すると、「トランザクションはトリガーで運命づけられました。バッチは中止されました。」というエラーが表示されます。

BEGIN TRY
    --Some More SQL
END TRY
BEGIN CATCH
    return
END CATCH

これを回避する方法はありますか?

14
Eldila

私の経験では、トリガーのtry catchでキャッチされたエラーは、トランザクション全体をロールバックします。保存トランザクションを使用できる場合があります。 「Somemoresql」で何が起こっているのかを見て、エラーを止めるためにcase/ifステートメントを記述できるかどうかを判断する必要があると思います。

何をしているかによっては、 トランザクションの保存 を使用して、それをキャッチに取り込むことができる場合があります。

あなたのコードではこのようなもの

SAVE TRANSACTION BeforeUpdate;
BEGIN TRY
        --Some More SQL
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION BeforeUpdate;
        return
END CATCH
8
u07ch

トリガーでロールバックしないでください。トランザクションを開始する必要もありません。

ROLLBACK TRANSACTIONは、元のDMLトリガーをロールバックしますおよび追加のトリガートランザクションも。したがって、バッチは中止されます

編集:

キャッチブロックに「RETURN」を含めず、コードを完了させることをお勧めします。トリガーでトラップされたエラーを無視したことはありません(ただし、ロールバックとレイザーを使用してトリガーでTRY/CATCHを使用して再スローします)。これは推測ですが、リターンはおそらくトリガーの異常な終了条件です

また、そもそもエラー状態を回避するようにしてください。エラーを回避するには、--some more sqlを変更してください。例、if exists(...を追加して、最初に重複などをテストします

7
gbn

SET XACT_ABORT OFFを使用します。Transact-SQLステートメントでエラーが発生すると、エラーメッセージが表示され、トランザクションは処理を続行します。次のコードは、トリガーを作成するためのものです。

Create TRIGGER [dbo].tr_Ins_Table_Master ON [dbo].Table_Master
 AFTER INSERT
AS
BEGIN
set xact_abort off
BEGIN TRY
        --your SQL          
        INSERT INTO Table_Detail
        SELECT MasterID,Name FROM INSERTED

END TRY

BEGIN CATCH     
    select ERROR_MESSAGE()
END CATCH

END
3
神來之筆

トリガーアクションの前にトランザクションデータが失われないようにするには、COMMITTRANを呼び出す必要があります。 TRY/CATCHブロックの前にこれを行うと、希望する結果が得られます。

例:

COMMIT TRAN
BEGIN TRY
    -- possible error occurs here...
END TRY
BEGIN CATCH
    PRINT 'Error on line ' + CAST(ERROR_LINE() AS VARCHAR(10))
    PRINT ERROR_MESSAGE()
END CATCH

それでも次のエラーがスローされます-回避方法がわかりません:

The transaction ended in the trigger. The batch has been aborted.

ただし、元のトランザクションとトリガートランザクションの両方が正常にコミットされるはずです。

更新:例外の最後のエラーを回避するには、TRYステートメント内でBEGINTRANを呼び出します。 Microsoftは、トリガー内でCOMMIT TRANを呼び出さないことをお勧めしますが、やむを得ない場合は、これでうまくいくはずです。

例:

COMMIT TRAN
BEGIN TRY
    BEGIN TRAN
2
rjchicago

このデモは、上記で求められた多くのことを実現します。エラーメッセージはオプションになります。それを機能させる秘訣は、ネストされた動的実行にあります。

    if object_id('toto')  is not  null drop table toto
    go
    create table toto (i int);
    go
    if object_id('toto2')  is not  null drop table toto2
    go
    create table toto2 (i int);
    go
    create Trigger trtoto
    ON toto
    Instead Of Insert
    as
    Begin
      BEGIN TRY
        set nocount  on
        insert into  toto  values(2)

        declare @sql nvarchar(max) =  'insert into toto2 values(3); select * from ThisTableDoesntexist'

        Exec sp_executeSql N'set xact_abort off; exec (@sql) ', N'@sql nvarchar(max)', @sql

      END TRY

      BEGIN CATCH
        PRINT  'Error on line ' + CAST(ERROR_LINE() AS VARCHAR(10))
        PRINT ERROR_MESSAGE()
      END CATCH
    End

GO
-- tests
set nocount on
insert into toto values (1)  -- is not inserted on purpose by  the trigger
select * from toto   -- other value inserted despite the error
select * from toto2  -- other value inserted in other table despite the error
1
Maurice Pelchat

最善の方法ではありませんが、機能します。新しいトランザクションを開始し、通常のコミットロールバックを実行して、暗黙的なトランザクションコミットのために最後に別のトランザクションを開始します

http://msdn.Microsoft.com/en-us/library/ms187844(v = SQL.90).aspx

1
Khawaja

トリガーで何をしようとしているのかを知ることが役立つ場合があります。

トリガーは、挿入または削除されたテーブルにデータを送信したトランザクションの一部です。失敗した場合は、トランザクション全体をロールバックします。トリガーがときどき失敗することを期待しているが、トリガーを起動させたステートメントをロールバックしない場合は、トリガーを使用するのが正しいかどうかを再考する必要があります。

1
HLGEM

u07ch、

残念ながら、保存トランザクションを使用して試行することはできません。一緒にキャッチします。単に一緒に機能することはできません。

http://sqlblog.com/blogs/alexander_kuznetsov/archive/2008/11/15/avoid-mixing-old-and-new-styles-of-error-handling.aspx

1
A-K

トリガーの開始時にXACT_AbortをOFFに設定できます。

0
Jami