エラー発生時のトランザクションのロールバック - SET XACT_ABORT ON と TRY...CATCH

エラー発生時のトランザクションのロールバック

BEGIN TRANSACTION と COMMIT TRANSACTION を使って、複数のステートメントをひとつのトランザクションとして実行したいとします。

今回は、そのトランザクションの途中でエラーが発生した時のロールバックのお話です。

今回使うテーブルやデータは こちら のスクリプトで生成できますが、同じ名前の既存のテーブルがあると削除されてしまうのでご注意ください。


SET XACT_ABORT ON と TRANSACTION

次のような Student テーブルと TestResult テーブルがあり、TestResult テーブルの StudentID カラムには Student テーブルの StudentID カラムへの外部キー制約が定義されています。

エラー発生時のトランザクションのロールバック 1

次のように、3 つの UPDATE ステートメントで TestResult テーブルの StudentID カラムを更新しますが、2 個目は存在しない StudentID を指定しているのでエラーになります。

UPDATE  TestResult
SET     StudentID = 4
WHERE   TestResultID = 1;

UPDATE  TestResult
SET     StudentID = 50
WHERE   TestResultID = 2;

UPDATE  TestResult
SET     StudentID = 6
WHERE   TestResultID = 3;

3 つのステートメントをひとつのトランザクションとして、エラーが起こった場合には、全部の更新をロールバックさせたいとします。

そんな時は BEGIN TRANSACTION と COMMIT TRANSACTION でひとつのトランザクションとしたいスクリプトをはさみますが、それだけではエラー発生時に全ての処理がロールバックされません。

試しに次のスクリプトを実行して結果をみてみましょう。

BEGIN TRANSACTION;

UPDATE  TestResult
SET     StudentID = 4
WHERE   TestResultID = 1;

UPDATE  TestResult
SET     StudentID = 50
WHERE   TestResultID = 2;

UPDATE  TestResult
SET     StudentID = 6
WHERE   TestResultID = 3;

COMMIT TRANSACTION;

[ 実行結果 ]
エラー発生時のトランザクションのロールバック 2
エラー発生時のトランザクションのロールバック 3

2 個目の UPDATE ステートメントでエラーが起こりましたが、1 個目と 3 個目の UPDATE ステートメントは実行され、値が更新されてしまいました。


トランザクションの途中でエラーが発生した時に、トランザクション内の処理を全てロールバックさせるには、BEGIN TRANSACTION の前に SET XACT_ABORT を ON に設定しておきます

それでは、StudentID の値を元に戻して、今度は SET XACT_ABORT ON を実行してから、同じスクリプトを実行してみます。

SET XACT_ABORT ON;
BEGIN TRANSACTION;

UPDATE  TestResult
SET     StudentID = 4
WHERE   TestResultID = 1;

UPDATE  TestResult
SET     StudentID = 50
WHERE   TestResultID = 2;

UPDATE  TestResult
SET     StudentID = 6
WHERE   TestResultID = 3;

COMMIT TRANSACTION;

[ 実行結果 ]
エラー発生時のトランザクションのロールバック 4
エラー発生時のトランザクションのロールバック 5

2 個目の UPDATE ステートメントが起こった時点で処理が終了してロールバックされ、TestResult テーブルの値は更新されませんでしたね。

TRY...CATCH を使ってロールバックする

TRY...CATCH を使っても、エラー発生した際に処理全部をロールバックさせることができます。

エラーを CATCH して何か処理をしたい際に便利です。

TRY...CATCH を使って、ひとつでもエラーが起こった場合に、次のようにトランザクション内の処理をロールバックさせることができます。

BEGIN TRY  

    BEGIN TRANSACTION;

        UPDATE  TestResult
        SET     StudentID = 4
        WHERE   TestResultID = 1;

        UPDATE  TestResult
        SET     StudentID = 50
        WHERE   TestResultID = 2;

        UPDATE  TestResult
        SET     StudentID = 6
        WHERE   TestResultID = 3;

    COMMIT TRANSACTION; 

END TRY  
BEGIN CATCH 

    ROLLBACK TRANSACTION;

END CATCH

[ 実行結果 ]
エラー発生時のトランザクションのロールバック 6
エラー発生時のトランザクションのロールバック 7

エラーはキャッチされ、TestResult テーブルの値も更新されていませんね。


私は、スクリプトを見ながら実行するような時は SET XACT_ABORT ON を、そうでない時はログを残したいので TRY...CATCH を使うことが多いです。

© 2010-2024 SQL Server 入門