エラー時にデータ不整合を防ぐ!SQL Serverのロールバック処理を安全に設計する方法
SQL Serverで複数の更新処理をまとめて実行する場合、
途中でエラーが発生すると一部だけが更新されてしまうリスクがあります。
これを防ぐために使うのがトランザクション制御とロールバック処理です。
この記事では、TRY...CATCH構文を利用した安全なロールバック設計を
実務向けにわかりやすく解説します。
目次
トランザクションとロールバックの基本
トランザクションとは?
トランザクションは、複数のSQL処理をひとまとまりとして扱う仕組みです。 すべての処理が成功すればコミット(確定)、どれかが失敗すればロールバック(取消)します。
基本構文
BEGIN TRANSACTION;
UPDATE Customers SET Balance = Balance - 1000 WHERE CustomerID = 1;
UPDATE Customers SET Balance = Balance + 1000 WHERE CustomerID = 2;
COMMIT TRANSACTION;
上記の2つの更新が両方成功した場合のみ確定されます。
ロールバック処理
途中でエラーが発生した場合、次のように戻せます。
ROLLBACK TRANSACTION;
これにより、実行前の状態に戻ります。
エラー時に部分更新を防ぐ仕組み
なぜTRY...CATCHが必要なのか
SQL Serverでは、単純にROLLBACKを書くだけではエラーを自動検知できません。 そこでTRY...CATCH構文を使い、エラー発生時に確実にROLLBACKする仕組みを構築します。
誤った例
BEGIN TRANSACTION;
UPDATE Products SET Price = Price / 0; -- エラー発生
ROLLBACK TRANSACTION; -- 実行されない
このように、エラーで処理が中断するとROLLBACKまで到達しません。
TRY...CATCH構文での基本設計
正しいロールバック構成例
BEGIN TRY
BEGIN TRANSACTION;
UPDATE Customers SET Balance = Balance - 500 WHERE CustomerID = 1;
UPDATE Customers SET Balance = Balance + 500 WHERE CustomerID = 2;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE @ErrMsg NVARCHAR(4000) = ERROR_MESSAGE();
PRINT 'ロールバック実行: ' + @ErrMsg;
END CATCH;
このようにすることで、例外が発生しても必ずROLLBACKが実行されます。
ネスト構造での注意点と@@TRANCOUNTの活用
ネストトランザクションの落とし穴
ストアドプロシージャ内でトランザクションを入れ子にする場合、
複数回のBEGIN TRANSACTIONでも1つのトランザクションとして扱われます。
@@TRANCOUNTの確認
PRINT '現在のトランザクション数: ' + CAST(@@TRANCOUNT AS NVARCHAR(10));
@@TRANCOUNTが1以上であればトランザクション中です。
CATCH内ではこれを確認してからROLLBACKを行うのが安全です。
RAISERRORとの組み合わせによるログ通知
ログ出力付きのCATCH構文
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO Sales VALUES (1, 1000, GETDATE());
INSERT INTO Sales VALUES (NULL, 2000, GETDATE()); -- エラー
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
DECLARE @ErrMsg NVARCHAR(4000) = ERROR_MESSAGE();
RAISERROR('エラー発生: %s', 16, 1, @ErrMsg) WITH LOG;
END CATCH;
WITH LOGを指定すれば、SQL Serverエラーログにも記録されます。
実務で使えるテンプレート構文
汎用トランザクション制御テンプレート
CREATE PROCEDURE usp_UpdateOrder
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRANSACTION;
-- 処理開始
UPDATE Orders SET Status = 'Completed' WHERE OrderID = 1001;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE
@ErrMsg NVARCHAR(4000) = ERROR_MESSAGE(),
@ErrProc NVARCHAR(128) = ERROR_PROCEDURE();
RAISERROR('処理失敗(%s): %s', 16, 1, @ErrProc, @ErrMsg) WITH LOG;
END CATCH;
END;
このようにプロシージャ単位で統一的に組み込むことで、 エラーログ・トランザクション整合性を両立できます。
まとめと次のステップ
学んだ内容の整理
- トランザクションは複数処理をひとまとめにし、整合性を確保する。
- TRY...CATCHを使うことで、エラー発生時も安全にROLLBACK可能。
- @@TRANCOUNTでトランザクションの状態を確認してから処理。
- RAISERRORと組み合わせてログ通知・監査を実現。
参考リンク

0 件のコメント:
コメントを投稿