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 を削除すると、事態はさらに奇妙になります。
PRINT ステートメントがまだ SSMS に表示されない
上記と同じエラーが正しく表示されます。
"メッセージ 208、レベル 16、状態 1、プロシージャ usp_clean_and_re_Insert、行 15 無効なオブジェクト名 'dbo.fn_DoSOmething'. "
- 次のような最終エラーがあります。
「メッセージ 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 なしで) ロールバックされませんか?
私は混乱しています!要約すれば:
SSMS に PRINT ステートメントが表示されないのはなぜですか?
ストアド プロシージャから XACT_ABORT ON を削除すると、CATCH ブロックの ROLLBACK TRANSACTION が実行されないのはなぜですか?
XACT_ABORT ON が単独で機能するように見えるのに、なぜ TRY...CACTH を使用するのでしょうか。つまり、Try..catch を削除して XACT_ABORT を ON のままにすると、トランザクションがロールバックされるので、catch ブロックで暗黙の ROLLBACK TRANSACTION を使用して TRY CATCH が必要になるのはなぜですか?