2026年2月3日火曜日

SQL Server エラー処理とTRY...CATCH構文の基本|トランザクションとの組み合わせと実務例

失敗しても安全に戻すには?SQL ServerのTRY...CATCHとエラー処理の基本をやさしく解説

実運用のSQLでは、常に理想通りに処理が進むとは限りません。
一意制約違反、外部キー違反、変換エラー、タイムアウトなど、エラーが起きたときに安全に処理を巻き戻し、原因を記録し、正しく再通知できる設計が大切です。
この記事では、SQL Server のTRY...CATCH 構文を中心に、エラー情報の取得関数、トランザクションとの組み合わせ、実務でのベストプラクティスを初心者向けに解説します。

目次

TRY...CATCH構文の基本

基本形


BEGIN TRY
    -- エラーが発生する可能性のある処理
END TRY
BEGIN CATCH
    -- エラー時の代替処理(ログ、後片付け、再スロー 等)
END CATCH;

TRY ブロック内で実行時エラーが発生すると制御は CATCH ブロックへ移り、CATCH 内ではエラーの内容を取得・処理できます。構文エラー(パースエラー)はTRYに入る前に失敗するため捕捉できません。

THROW と RAISERROR

  • THROW:SQL Server 2012以降で推奨。元エラーをそのまま再送出でき、書式も簡潔。
  • RAISERROR:レガシー手法。新規実装では THROW の利用が基本。

エラー情報を取得する関数

CATCH ブロック内で使える代表的な関数は以下です。

関数名 取得内容
ERROR_NUMBER() エラー番号 2627(一意制約違反 など)
ERROR_MESSAGE() エラーメッセージ 詳細なテキスト(NVARCHAR(2048))
ERROR_SEVERITY() 重大度 11〜16など(ユーザー修正可能は 11〜16)
ERROR_STATE() 状態コード サポートへの手掛かり
ERROR_LINE() エラー発生行 デバッグ用
ERROR_PROCEDURE() 手続き名 SP/トリガー名など

トランザクションとの組み合わせ(ROLLBACK/COMMIT)

定石パターン


BEGIN TRY
    BEGIN TRANSACTION;

    -- 更新系処理(INSERT/UPDATE/DELETE など)

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0
        ROLLBACK TRANSACTION;  -- 可能なら必ず巻き戻す

    -- ログ出力など(後述のサンプル参照)

    THROW;  -- 呼び出し元へ再通知(推奨)
END CATCH;

XACT_STATE() は現在のトランザクション状態を返します(1=コミット可能/-1=コミット不可/0=なし)。エラー時はまず安全に ROLLBACK できるか判断しましょう。

SET XACT_ABORT ON の活用

外部キー違反など一部のエラーで自動ロールバックを強制したい場合、バッチ先頭で SET XACT_ABORT ON; を指定すると堅牢性が高まります(ただし適用範囲に留意)。

実務で使えるエラーハンドリング例

① 基本:ログテーブルに記録して再スロー


-- 例)簡易ログテーブル
-- CREATE TABLE dbo.ErrorLog (
--   Id INT IDENTITY PRIMARY KEY,
--   ErrorNumber INT, Severity INT, State INT,
--   ProcName SYSNAME NULL, Line INT,
--   Message NVARCHAR(2048),
--   CreatedAt DATETIME2 DEFAULT SYSUTCDATETIME()
-- );

BEGIN TRY
    BEGIN TRAN;

    -- 例:在庫引当
    UPDATE Inventory SET Stock = Stock - 1 WHERE ProductID = @pid;

    COMMIT;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 ROLLBACK;

    INSERT INTO dbo.ErrorLog
    (ErrorNumber, Severity, State, ProcName, Line, Message)
    VALUES
    (ERROR_NUMBER(), ERROR_SEVERITY(), ERROR_STATE(),
     ERROR_PROCEDURE(), ERROR_LINE(), ERROR_MESSAGE());

    THROW;  -- 上位へ通知
END CATCH;

② ストアドでの標準テンプレート


CREATE OR ALTER PROCEDURE dbo.usp_UpdateOrder
  @OrderID INT, @Amount DECIMAL(10,2)
AS
SET NOCOUNT ON;
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRAN;

    UPDATE Orders
      SET Amount = @Amount
      WHERE OrderID = @OrderID;

    COMMIT;
END TRY
BEGIN CATCH
    IF XACT_STATE() <> 0 ROLLBACK;

    DECLARE @msg NVARCHAR(2048) = ERROR_MESSAGE();
    DECLARE @num INT = ERROR_NUMBER();

    -- 必要に応じてログ
    INSERT INTO dbo.ErrorLog(ErrorNumber, Severity, State, ProcName, Line, Message)
    VALUES (@num, ERROR_SEVERITY(), ERROR_STATE(), ERROR_PROCEDURE(), ERROR_LINE(), @msg);

    -- ビジネス用途ならメッセージを整形して再通知も可
    THROW;  -- そのまま原文で再送出(推奨)
END CATCH;

③ 業務的な再送出(サニタイズ)

内部事情を隠したい場合は、アプリ層向けの定型メッセージで再送出します。


BEGIN CATCH
    IF XACT_STATE() <> 0 ROLLBACK;

    -- ログは詳細に
    INSERT INTO dbo.ErrorLog(...);

    -- 利用者には簡潔に
    THROW 50001, N'更新に失敗しました。しばらくしてから再実行してください。', 1;
END CATCH;

よくある注意点とベストプラクティス

  • TRY 内での構文エラーは捕捉できない:デプロイ前に必ずコンパイル検証。ユニットテストを整備。
  • 必ず ROLLBACK 可能か確認:XACT_STATE() で状態を判定してからロールバック。
  • THROW を基本に:新規実装は THROW を使い、元のスタック情報を保持。
  • SET XACT_ABORT ON の活用:更新系バッチ・ストアドでの安全性向上に有効。
  • 再実行可能な設計:同一リクエストの二重実行を想定(冪等キー・楽観ロック等)。
  • 監査ログは十分な項目で:ユーザーID、関連ID、入力値、アプリ識別子、UTC日時。

まとめと次のステップ

学んだ内容の整理

  • TRY...CATCH で実行時エラーを捕捉し、ログと後処理を行える。
  • ERROR_* 関数で詳細なエラー情報を取得し、原因調査に活用。
  • トランザクションと組み合わせ、XACT_STATE() で確実に ROLLBACK/COMMIT を制御。
  • 実務では THROW を基本に、SET XACT_ABORT ON とログ設計で堅牢化。

参考リンク

SQL Server 解説用イメージ

0 件のコメント:

コメントを投稿