例外処理の方法をまとめておきたかったので、SQL Server 2005 以降でサポートされている例外処理の記述サンプルを掲載します。

確認環境

  • SQL Server 2005 SP2
  • 実行環境 Windows Server 2003
  • 使用データベース AdventureWorks

例外処理の解説については下記リンクを参照
Transact-SQL での TRY...CATCH の使用 
http://msdn.microsoft.com/ja-jp/library/ms179296.aspx 
Transact-SQL のエラー情報の取得 
http://msdn.microsoft.com/ja-jp/library/ms179495.aspx

1. 例外処理サンプル1

TRY .. CATCH を使用したT-SQLサンプルです。重複エラーを発生させるために、IDENTITY 列に値を設定するためにSET IDENTITY_INSERT を故意に設定してあります。サンプルでは例外をキャッチして、例外情報を取得し、RAISERROR関数で再度例外をスローするようにしています。

USE [AdventureWorks]
GO

/* TRY..CATCH を使用した例外処理
     参考リンク
     Transact-SQL での TRY...CATCH の使用
      http://msdn.microsoft.com/ja-jp/library/ms179296.aspx
     Transact-SQL のエラー情報の取得
      http://msdn.microsoft.com/ja-jp/library/ms179495.aspx
 */
SET NOCOUNT ON;
  -- 重複エラーを出すために故意に設定
 SET IDENTITY_INSERT AdventureWorks.Sales.SalesReason ON;
GO

BEGIN TRY

  INSERT INTO  Sales.SalesReason(SalesReasonID, Name, ReasonType, ModifiedDate)
  VALUES (1, 'Name1', 'ReasonType1', GetDate());
END TRY
BEGIN CATCH
  DECLARE @ErrorMessage NVARCHAR(4000)
               , @ErrorNumber   INT
               , @ErrorSeverity  INT
               , @ErrorState      INT
               , @ErrorLine        INT
               , @ErrorProcedure NVARCHAR(126)
  ;
  -- TRY...CATCH 構造の CATCH ブロックが実行される原因となったエラーのメッセージ テキストを返します。
  SET @ErrorMessage = ERROR_MESSAGE()
  -- TRY...CATCH 構造の CATCH ブロックの実行を引き起こしたエラーのエラー番号を返します。
  -- @@error に対応する関数
  SET @ErrorNumber =  ERROR_NUMBER()
  -- TRY...CATCH 構造の CATCH ブロックの実行を引き起こしたエラーの重大度を返します。
  SET @ErrorSeverity = ERROR_SEVERITY()
  -- TRY...CATCH 構造の CATCH ブロックが実行された原因となるエラーの状態番号を返します。
  SET @ErrorState = ERROR_STATE()
  -- TRY...CATCH 構造の CATCH ブロックの実行を引き起こすエラーが発生した行番号を返します。
  SET @ErrorLine =  ERROR_LINE()
  -- TRY...CATCH 構造の CATCH ブロックの実行原因となったエラーが発生した、
  -- ストアド プロシージャまたはトリガの名前を返します。
  SET @ErrorProcedure = ERROR_PROCEDURE()

  -- 実行確認用
  PRINT N'ERROR_NUMBER() ';
  PRINT @ErrorNumber;
  PRINT N'ERROR_MESSAGE() ';
  PRINT @ErrorMessage;
  PRINT N'ERROR_SEVERITY() ';
  PRINT @ErrorSeverity;
  PRINT N'ERROR_STATE() ';
  PRINT @ErrorState;
  PRINT N'ERROR_LINE() ';
  PRINT @ErrorLine;
  PRINT N'ERROR_PROCEDURE() ';
  PRINT @ErrorProcedure;

  RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

2. セーブポイントを使用した、例外処理

呼び出し側のトランザクションの有無によってトランザクションのロールバックか、セーブポイントへのロールバックかを決定する処理を追加したT-SQLのサンプルを掲載します。サンプルではさらに、XACT_STATE() を使用してコミット可否を判定するようにしています。

USE [AdventureWorks]
GO

/* セーブポイントを使用した、例外処理
     呼び出し側のトランザクションの有無によって
     トランザクションのロールバックか、セーブポイントへのロールバック
     かを決定する。
     XACT_STATE() を使用してコミット可否を判定する

     参考リンク
     Transact-SQL での TRY...CATCH の使用
      http://msdn.microsoft.com/ja-jp/library/ms179296.aspx
     Transact-SQL のエラー情報の取得
      http://msdn.microsoft.com/ja-jp/library/ms179495.aspx
 */
SET NOCOUNT ON;
-- 重複エラーを出すために故意に設定
SET IDENTITY_INSERT AdventureWorks.Sales.SalesReason ON;
-- 制約エラーもコミットできないエラーとする
SET XACT_ABORT ON;
-- ロックのタイムアウトを0秒に
SET LOCK_TIMEOUT 5000;
GO

-- トランザクション数をカウント
DECLARE @tc AS INT;
SET @tc = @@trancount;

IF @tc > 0
  SAVE TRANSACTION S1;
ELSE
  BEGIN TRANSACTION

BEGIN TRY
    INSERT INTO  Sales.SalesReason(SalesReasonID, Name, ReasonType, ModifiedDate)
           VALUES (1, 'Name1', 'ReasonType1', GetDate());
    INSERT INTO  Sales.SalesReason(SalesReasonID, Name, ReasonType, ModifiedDate)
           VALUES (2, 'Name1', 'ReasonType1', GetDate());
  COMMIT TRANSACTION
END TRY
BEGIN CATCH
  PRINT N'例外発生'
  DECLARE @ErrorMessage NVARCHAR(4000)
               , @ErrorNumber   INT
               , @ErrorSeverity  INT
               , @ErrorState      INT
               , @ErrorLine        INT
               , @ErrorProcedure NVARCHAR(126)
  ;
  -- TRY...CATCH 構造の CATCH ブロックが実行される原因となったエラーのメッセージ テキストを返します。
  SET @ErrorMessage = ERROR_MESSAGE()
  -- TRY...CATCH 構造の CATCH ブロックの実行を引き起こしたエラーのエラー番号を返します。
  -- @@error に対応する関数
  SET @ErrorNumber =  ERROR_NUMBER()
  -- TRY...CATCH 構造の CATCH ブロックの実行を引き起こしたエラーの重大度を返します。
  SET @ErrorSeverity = ERROR_SEVERITY()
  -- TRY...CATCH 構造の CATCH ブロックが実行された原因となるエラーの状態番号を返します。
  SET @ErrorState = ERROR_STATE()
  -- TRY...CATCH 構造の CATCH ブロックの実行を引き起こすエラーが発生した行番号を返します。
  SET @ErrorLine =  ERROR_LINE()
  -- TRY...CATCH 構造の CATCH ブロックの実行原因となったエラーが発生した、
  -- ストアド プロシージャまたはトリガの名前を返します。
  SET @ErrorProcedure = ERROR_PROCEDURE()

  IF @tc = 0
    -- トランザクションを開始した場合
    BEGIN
      IF (XACT_STATE()) <> 0 
        BEGIN
          PRINT N'ロールバックを行います'
          ROLLBACK TRANSACTION
        END
    END
  ELSE
    -- セーブポイントを設定した場合
    BEGIN
      IF (XACT_STATE()) = 1
        BEGIN
          PRINT N'本処理中の処理のみロールバックします'
          ROLLBACK TRANSACTION S1;
        END
      ELSE IF (XACT_STATE()) = -1
         BEGIN
           -- ロールバックできない状態. 処理は呼び出し側に委任する
           PRINT N'トランザクションはコミットできない状態です。'
         END
    END
  -- 呼び出し元に委任
  RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
END CATCH

3. まとめ

解説は以上です。SQL Server 2000 の場合は、@@error の内容で確認していましたが、構造的でわかりやすい例外処理ができるようになったと思います。