0

XACT_ABORT ON を TRY...CATCH コンストラクトと一緒に使用して、TRY ブロックでエラーが発生したときに CATCH ブロックでトランザクションをロールバックしようとすることについて、少し混乱しています。

私はこのように構造化されたストアドプロシージャを持っています(もちろんここでは単純化されています):

CREATE PROCEDURE dbo.usp_clean_and_re_Insert
AS
   SET XACT_ABORT ON;

   BEGIN TRY

      BEGIN TRANSACTION

      -- first clear the table
      DELETE FROM dbo.table1

      -- re-populate the table
      INSERT INTO dbo.table1
      (col1, col2, col3)
      SELECT  1
              ,dbo.fn_DoSomething('20150101')
              ,dbo.fn_DoSomething('20150123')

      COMMIT TRANSACTION

   END TRY

BEGIN CATCH
-- Test XACT_STATE for 0, 1, or -1.
-- If 1, the transaction is committable.
-- If -1, the transaction is uncommittable and should 
--     be rolled back.
-- XACT_STATE = 0 means there is no transaction and
--     a commit or rollback operation would generate an error.

-- Test whether the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
    PRINT 'The transaction is in an uncommittable state.' +
          ' Rolling back transaction.'
    ROLLBACK TRANSACTION;
END;

-- Test whether the transaction is active and valid.
IF (XACT_STATE()) = 1
BEGIN
    PRINT 'The transaction is committable.' + 
          ' Committing transaction.'
    COMMIT TRANSACTION;   
END;
END CATCH;

したがって、SP は次のように動作するように意図されています。トランザクションがいずれかの時点で失敗した場合、ロールバックする必要があります。そのため、挿入ビットが失敗した場合、削除ビットをロールバックする必要があります。つまり、テーブルは以前と同じ状態にする必要があります。

ここで、実行時に dbo.fn_DoSomething() 関数が使用できないとします (DBA によって誤って削除されました)。上記の SP は期待どおりに動作します。つまり、トランザクションはロールバックされ、テーブルはそのまま残り、SSMS に表示されるエラー メッセージは次のようになります。

"メッセージ 208、レベル 16、状態 1、プロシージャ usp_clean_and_re_Insert、行 15 無効なオブジェクト名 'dbo.fn_DoSOmething'. "

しかし、何らかの理由で CATCH ブロックからの PRINT ステートメントが実行されないようです。つまり、SSMS でそれらを見ることができませんか? TRY...CATCH に関する Microsoft のドキュメントによると、TRY ブロックでの実行中にエラーが発生した場合、実行は CATCH ブロックに渡されます ( https://msdn.microsoft.com/en-us/library/ms175976(v=sql .90).aspx )。

ただし、XACT_ABORT ON を削除すると、事態はさらに奇妙になります。

  1. PRINT ステートメントがまだ SSMS に表示されない

  2. 上記と同じエラーが正しく表示されます。

"メッセージ 208、レベル 16、状態 1、プロシージャ usp_clean_and_re_Insert、行 15 無効なオブジェクト名 'dbo.fn_DoSOmething'. "

  1. 次のような最終エラーがあります。

「メッセージ 266、レベル 16、状態 2、手順 usp_clean_and_re_Insert、EXECUTE 後の行 52 トランザクション数は、COMMIT または ROLLBACK TRANSACTION ステートメントが欠落していることを示しています。前の数 = 0、現在の数 = 1。」

これにより、SSMS (SP が実行されたクエリ ウィンドウ) を切断するまでテーブルがロックされます。その後、すべての結果がそのままの状態でテーブルが再び使用可能になります (したがって、DB エンジンはコミットできないトランザクションを暗黙的にロールバックする必要があります)。

このエラー メッセージに関する他の投稿 (次のようなもの: Transaction count after EXECUTE shows a mismatched number of BEGIN and COMMIT statement. Previous count = 1, current count = 0 ) を読んで、CATCH ブロックの XACT_STATE を確認する必要があることを理解しました。コミット不可能なトランザクションをロールバックします(これはhttps://msdn.microsoft.com/en-us/library/ms189797.aspxからの同じアドバイスです)が、これはまさに私が上記の SP で行ったことであり、トランザクションSSMS を切断するまで (XACT_ABORT ON なしで) ロールバックされませんか?

私は混乱しています!要約すれば:

  1. SSMS に PRINT ステートメントが表示されないのはなぜですか?

  2. ストアド プロシージャから XACT_ABORT ON を削除すると、CATCH ブロックの ROLLBACK TRANSACTION が実行されないのはなぜですか?

  3. XACT_ABORT ON が単独で機能するように見えるのに、なぜ TRY...CACTH を使用するのでしょうか。つまり、Try..catch を削除して XACT_ABORT を ON のままにすると、トランザクションがロールバックされるので、catch ブロックで暗黙の ROLLBACK TRANSACTION を使用して TRY CATCH が必要になるのはなぜですか?

4

1 に答える 1

0

この場合、実際にはまだトランザクションを開始していないため、XACT_ABORT は機能しないと思いますが、間違っている可能性もあります。関数が存在しません。これは、DB に触れる前に、SQL Server がトランザクションに失敗することを意味します。XACT_STATE のマニュアル ページと提供されている例を読むと、読み取り/書き込みで実際に失敗する必要があるようです。オプティマイザーが構文エラー (存在しない関数が呼び出され、実行するまでに作成していない) があることを確認するため、クエリはそれほどうまくいきません。

このページ (XACT_STATE のマニュアル ページ) と利用可能な例を読むと、データの変更が実際に試行されたときにのみ発生する、制約エラーが発生した場合にのみ XACT_STATE が設定されることが強く示唆されています。

https://msdn.microsoft.com/en-us/library/ms189797.aspx

テストする方法として、プロファイラーで dbo.table1 のトランザクションを監視することができます。クエリで XACT_ABORT ON を使用すると、削除は行われないと思います。削除すると、オプティマイザは、削除の実行を許可する別の実行計画を選択する可能性があります。

于 2015-01-23T16:18:31.060 に答える