web-dev-qa-db-ja.com

TRY CATCHが使用されるSQL Serverでトランザクションをコミットするためのベストプラクティス

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ブロックの外でコミットを行うときにエラーが発生する可能性はありますか?

4
user20358

オプションAが正しい選択です。トランザクション内のすべてのステートメントが機能し、実際のCOMMITが失敗する可能性があるため、COMMITの失敗をキャッチし、このエラーとロールバックを適切に処理できるように、TRYブロック内にCOMMITを保持します。

SEの this answer を参照してください。コード例では、他のユーザーがこの手順を同時に実行していると想定すると、TRYブロックが正常に完了し、他のセッションによる変更が原因でCOMMITに失敗する可能性があります。

3
HandyD

これを行うために私が見つけた最良の方法は次のコードです:

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コンストラクトでは発生しないエラーをキャッチするのに役立ちます。

1
George.Palacios