SQL Serverコードブロックで、コミットトランザクションを配置するのに最適な場所はどこですか? try catchブロックの内部または外部?.
たとえば、オプションAまたはオプションBは正しいアプローチですか、それとも主観的な選択ですか?
オプションA
CREATE PROCEDURE DummyProc
BEGIN TRY
BEGIN TRANSACTION
INSERT sometable(a, b) VALUES (@a, @b)
INSERT sometable(a, b) VALUES (@b, @a)
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@trancount > 0 ROLLBACK TRANSACTION
DECLARE @msg nvarchar(2048) = error_message()
RAISERROR (@msg, 16, 1)
RETURN 55555
END CATCH
オプションB
CREATE PROCEDURE DummyProc
BEGIN TRY
BEGIN TRANSACTION
INSERT sometable(a, b) VALUES (@a, @b)
INSERT sometable(a, b) VALUES (@b, @a)
END TRY
BEGIN CATCH
IF @@trancount > 0 ROLLBACK TRANSACTION
DECLARE @msg nvarchar(2048) = error_message()
RAISERROR (@msg, 16, 1)
RETURN 55555
END CATCH
IF @@trancount > 0 COMMIT TRANSACTION
オプションBでは、TRY-CATCH
ブロックの外でコミットを行うときにエラーが発生する可能性はありますか?
オプションAが正しい選択です。トランザクション内のすべてのステートメントが機能し、実際のCOMMITが失敗する可能性があるため、COMMITの失敗をキャッチし、このエラーとロールバックを適切に処理できるように、TRYブロック内にCOMMITを保持します。
SEの this answer を参照してください。コード例では、他のユーザーがこの手順を同時に実行していると想定すると、TRYブロックが正常に完了し、他のセッションによる変更が原因でCOMMITに失敗する可能性があります。
これを行うために私が見つけた最良の方法は次のコードです:
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION
/*
Code goes here
*/
COMMIT TRANSACTION
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
-- If >= SQL 2012 replace all code in catch block above with
-- THROW;
WHILE @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
END
END CATCH
エラーが効果的にキャッチされるようにするXACT_ABORTの使用、BEGINステートメントとCOMMITステートメントの両方がTRYブロック内にあるという事実、および@@ TrancountのWHILE-ネストされたトランザクションが確実にロールバックされるようにする必要があります(常に適用できるとは限りません)。
THROWステートメントは、2012年より前のSQLバージョンのRAISERRORを置き換えて、キャッチされた例外/エラーを再スローすることもできます。
Dan Guzmanがコメントで述べているように、XACT_ABORTは、タイムアウト、ランタイム照合エラーなど、TRY/CATCHコンストラクトでは発生しないエラーをキャッチするのに役立ちます。