TRY...CATCH (Transact-SQL)

Transact-SQL のエラー処理を実装します。これは Microsoft Visual C# 言語および Microsoft Visual C++ 言語での例外処理に似ています。Transact-SQL ステートメントのグループを TRY ブロックで囲むことができます。TRY ブロック内でエラーが発生すると、CATCH ブロックで囲まれた別のステートメントのグループに制御が渡されます。

トピック リンク アイコンTransact-SQL 構文表記規則

構文

BEGIN TRY
     { sql_statement | statement_block }
END TRY
BEGIN CATCH
          [ { sql_statement | statement_block } ]
END CATCH
[ ; ]

引数

  • sql_statement
    任意の Transact-SQL ステートメントです。

  • statement_block
    バッチ内、または BEGIN...END ブロックで囲まれた Transact-SQL ステートメントの任意のグループです。

説明

TRY...CATCH 構造は、データベース接続を閉じない、重大度が 10 を超えるすべての実行エラーを検出します。

TRY ブロックの直後には、関連する CATCH ブロックを記述する必要があります。END TRY ステートメントと BEGIN CATCH ステートメントの間に他のステートメントを含めると、構文エラーが生成されます。

TRY...CATCH 構造は複数のバッチをまたぐことはできません。TRY...CATCH 構造は、Transact-SQL ステートメントの複数のブロックをまたぐことはできません。たとえば、TRY...CATCH 構造で Transact-SQL ステートメントの 2 つの BEGIN...END ブロックをまたいだり、IF...ELSE 構造をまたいだりすることはできません。

TRY ブロックで囲まれたコードでエラーが発生しなかった場合は、TRY ブロック内の最後のステートメントの実行が完了すると、END CATCH ステートメントの直後にあるステートメントに制御が渡されます。TRY ブロックで囲まれたコードでエラーが発生した場合は、CATCH ブロック内の最初のステートメントに制御が渡されます。END CATCH ステートメントがストアド プロシージャまたはトリガ内の最後のステートメントである場合は、そのストアド プロシージャの呼び出しまたはトリガの起動を実行したステートメントに制御が渡されます。

CATCH ブロック内のコードが完了すると、END CATCH ステートメントの直後にあるステートメントに制御が渡されます。CATCH ブロックによってトラップされたエラーは、呼び出し元のアプリケーションに返されません。エラー情報の一部をアプリケーションに返す必要がある場合は、CATCH ブロック内のコードでその処理を行います。これには、SELECT 結果セットや、RAISERROR ステートメントおよび PRINT ステートメントなどのメカニズムを使用します。TRY...CATCH で RAISERROR を使用する方法の詳細については、「Transact-SQL での TRY...CATCH の使用」を参照してください。

TRY...CATCH 構造は入れ子にすることができます。TRY ブロックと CATCH ブロックのどちらにも、入れ子になった TRY...CATCH 構造を含めることができます。たとえば、CATCH ブロックに TRY...CATCH 構造を埋め込み、CATCH コードによって発生したエラーを処理することができます。

CATCH ブロックで発生したエラーは、他の場所で生成されたエラーと同じように扱われます。CATCH ブロックに入れ子になった TRY...CATCH 構造が含まれている場合、入れ子になった TRY ブロックでエラーが発生すると、入れ子になった CATCH ブロックに制御が渡されます。入れ子になった TRY...CATCH 構造が存在しない場合、エラーは呼び出し元に返されます。

TRY...CATCH 構造では、TRY ブロック内のコードによって実行されたストアド プロシージャまたはトリガからの処理されないエラーが検出されます。また、ストアド プロシージャまたはトリガに独自の TRY...CATCH 構造を含めて、それらのコードによって生成されたエラーを処理することもできます。たとえば、TRY ブロックでストアド プロシージャを実行し、そのストアド プロシージャでエラーが発生した場合、次の方法でエラーを処理できます。

  • ストアド プロシージャに独自の TRY...CATCH 構造が含まれていない場合、エラーが発生すると、EXECUTE ステートメントを含んでいる TRY ブロックに関連付けられた CATCH ブロックに制御が返されます。

  • ストアド プロシージャに TRY...CATCH 構造が含まれている場合、エラーによってストアド プロシージャ内の CATCH ブロックに制御が渡されます。CATCH ブロックのコードが完了すると、ストアド プロシージャを呼び出した EXECUTE ステートメントの直後にあるステートメントに制御が返されます。

GOTO ステートメントを使用して TRY ブロックまたは CATCH ブロックに入ることはできません。GOTO ステートメントは、同じ TRY ブロックまたは CATCH ブロック内のラベルに移動したり、TRY ブロックまたは CATCH ブロックから抜けるために使用できます。

TRY...CATCH 構造をユーザー定義関数内で使用することはできません。

エラー情報の取得

CATCH ブロックのスコープ内では、次のシステム関数を使用して、CATCH ブロックが実行される原因となったエラーに関する情報を取得できます。

  • ERROR_NUMBER() は、エラーの番号を返します。

  • ERROR_SEVERITY() は、重大度を返します。

  • ERROR_STATE() は、エラー状態番号を返します。

  • ERROR_PROCEDURE() は、エラーが発生したストアド プロシージャまたはトリガの名前を返します。

  • ERROR_LINE() は、エラーを発生させたルーチン内の行番号を返します。

  • ERROR_MESSAGE() は、エラー メッセージの完全なテキストを返します。このテキストには、長さ、オブジェクト名、時間など、代替可能なパラメータに対して提供された値が含まれます。

CATCH ブロックのスコープの外側から呼び出されると、これらの関数は NULL を返します。エラー情報は、CATCH ブロックのスコープ内のどこからでも、これらの関数を使用して取得できます。たとえば、次のスクリプトでは、エラー処理関数を含んでいるストアド プロシージャを示しています。TRY...CATCH 構造の CATCH ブロックでは、このストアド プロシージャを呼び出して、エラーに関する情報を返しています。

USE AdventureWorks;
GO
-- Verify that the stored procedure does not already exist.
IF OBJECT_ID ( 'usp_GetErrorInfo', 'P' ) IS NOT NULL 
    DROP PROCEDURE usp_GetErrorInfo;
GO

-- Create procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
SELECT
    ERROR_NUMBER() AS ErrorNumber
    ,ERROR_SEVERITY() AS ErrorSeverity
    ,ERROR_STATE() AS ErrorState
    ,ERROR_PROCEDURE() AS ErrorProcedure
    ,ERROR_LINE() AS ErrorLine
    ,ERROR_MESSAGE() AS ErrorMessage;
GO

BEGIN TRY
    -- Generate divide-by-zero error.
    SELECT 1/0;
END TRY
BEGIN CATCH
    -- Execute error retrieval routine.
    EXECUTE usp_GetErrorInfo;
END CATCH;

TRY...CATCH 構造の影響を受けないエラー

TRY...CATCH 構造では、次の条件はトラップされません。

  • 重大度が 10 以下の警告または情報メッセージ。

  • 重大度が 20 以上で、そのセッションの SQL Server データベース エンジン タスク処理を終了させるエラー。重大度が 20 以上のエラーが発生し、データベース接続が切断されない場合、TRY...CATCH によってエラーが処理されます。

  • クライアントの割り込み要求や中断されたクライアント接続などのアテンション。

  • システム管理者による KILL ステートメントを使用したセッションの終了。

次の種類のエラーは、TRY...CATCH 構造と同じ実行レベルで発生した場合には、CATCH ブロックによって処理されません。

  • 構文エラーなど、バッチの実行を妨げるコンパイル エラー。

  • ステートメントレベルの再コンパイルで発生するエラー (コンパイル後の名前の遅延解決により発生するオブジェクト名の解決エラーなど)。

これらのエラーは、バッチ、ストアド プロシージャ、またはトリガを実行したレベルに返されます。

TRY ブロック内の下位の実行レベル (たとえば、sp_executesql またはユーザー定義のストアド プロシージャを実行しているとき) でのコンパイル中またはステートメントレベルの再コンパイル中にエラーが発生した場合、そのエラーは TRY...CATCH 構造よりも下位のレベルで発生し、関連する CATCH ブロックによって処理されます。詳細については、「Transact-SQL での TRY...CATCH の使用」を参照してください。

次の例は、SELECT ステートメントによって生成されたオブジェクト名解決エラーが TRY...CATCH 構造でキャッチされず、同じ SELECT ステートメントをストアド プロシージャ内で実行した場合には CATCH ブロックでキャッチされることを示しています。

USE AdventureWorks;
GO

BEGIN TRY
    -- Table does not exist; object name resolution
    -- error not caught.
    SELECT * FROM NonexistentTable;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH

このエラーはキャッチされず、制御は TRY...CATCH 構造から出て、1 つ上位のレベルに渡されます。

この SELECT ステートメントをストアド プロシージャ内で実行すると、エラーは TRY ブロックよりも下位のレベルで発生します。このエラーは TRY...CATCH 構造によって処理されます。

-- Verify that the stored procedure does not exist.
IF OBJECT_ID ( N'usp_ExampleProc', N'P' ) IS NOT NULL 
    DROP PROCEDURE usp_ExampleProc;
GO

-- Create a stored procedure that will cause an 
-- object resolution error.
CREATE PROCEDURE usp_ExampleProc
AS
    SELECT * FROM NonexistentTable;
GO

BEGIN TRY
    EXECUTE usp_ExampleProc;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
  • バッチの詳細については、「バッチ」を参照してください。

コミット不可能なトランザクションと XACT_STATE

TRY ブロックで生成されたエラーによって現在のトランザクションの状態が無効になる場合、トランザクションはコミット不可能なトランザクションとして分類されます。通常は TRY ブロックの外部でトランザクションを終了させるエラーが、TRY ブロックの内部で発生すると、トランザクションはコミット不可能な状態になります。コミット不可能なトランザクションでは、読み取り操作または ROLLBACK TRANSACTION のみを実行できます。このトランザクションで、書き込み操作または COMMIT TRANSACTION を生成する Transact-SQL ステートメントを実行することはできません。XACT_STATE 関数は、トランザクションがコミット不可能なトランザクションと分類されている場合、値 -1 を返します。バッチが完了すると、データベース エンジンによってコミット不可能なトランザクションがロールバックされます。トランザクションがコミット不可能な状態になったときにエラー メッセージが送信されなかった場合、バッチが完了すると、エラー メッセージがクライアント アプリケーションに送信されます。これは、コミット不可能なトランザクションが検出され、ロールバックされたことを示します。

コミット不可能なトランザクションと XACT_STATE 関数の詳細については、「Transact-SQL での TRY...CATCH の使用」および「XACT_STATE (Transact-SQL)」を参照してください。

A. TRY...CATCH を使用する

次の例は、0 除算エラーを生成する SELECT ステートメントを示しています。このエラーにより、関連する CATCH ブロックに実行が移動します。

USE AdventureWorks;
GO

BEGIN TRY
    -- Generate a divide-by-zero error.
    SELECT 1/0;
END TRY
BEGIN CATCH
    SELECT
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() AS ErrorState
        ,ERROR_PROCEDURE() AS ErrorProcedure
        ,ERROR_LINE() AS ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO

B. トランザクション内で TRY...CATCH を使用する

次の例では、トランザクション内での TRY...CATCH ブロックの動作を示しています。TRY ブロック内のステートメントにより、制約違反エラーが生成されます。

USE AdventureWorks;
GO
BEGIN TRANSACTION;

BEGIN TRY
    -- Generate a constraint violation error.
    DELETE FROM Production.Product
    WHERE ProductID = 980;
END TRY
BEGIN CATCH
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() AS ErrorState
        ,ERROR_PROCEDURE() AS ErrorProcedure
        ,ERROR_LINE() AS ErrorLine
        ,ERROR_MESSAGE() AS ErrorMessage;

    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
END CATCH;

IF @@TRANCOUNT > 0
    COMMIT TRANSACTION;
GO

C. TRY...CATCH と XACT_STATE を使用する

次の例では、TRY...CATCH 構造を使用して、トランザクション内で発生するエラーを処理する方法を示しています。XACT_STATE 関数により、トランザクションをコミットすべきか、またはロールバックすべきかが決定されます。この例では SET XACT_ABORT は ON です。これにより、制約違反エラーが発生したときに、トランザクションはコミット不可能になります。

USE AdventureWorks;
GO

-- Check to see whether this stored procedure exists.
IF OBJECT_ID (N'usp_GetErrorInfo', N'P') IS NOT NULL
    DROP PROCEDURE usp_GetErrorInfo;
GO

-- Create procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
    SELECT 
        ERROR_NUMBER() AS ErrorNumber
        ,ERROR_SEVERITY() AS ErrorSeverity
        ,ERROR_STATE() AS ErrorState
        ,ERROR_LINE () AS ErrorLine
        ,ERROR_PROCEDURE() AS ErrorProcedure
        ,ERROR_MESSAGE() AS ErrorMessage;
GO

-- SET XACT_ABORT ON will cause the transaction to be uncommittable
-- when the constraint violation occurs. 
SET XACT_ABORT ON;

BEGIN TRY
    BEGIN TRANSACTION;
        -- A FOREIGN KEY constraint exists on this table. This 
        -- statement will generate a constraint violation error.
        DELETE FROM Production.Product
            WHERE ProductID = 980;

    -- If the DELETE statement succeeds, commit the transaction.
    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    -- Execute error retrieval routine.
    EXECUTE usp_GetErrorInfo;

    -- Test XACT_STATE:
        -- If 1, the transaction is committable.
        -- If -1, the transaction is uncommittable and should 
        --     be rolled back.
        -- XACT_STATE = 0 means that there is no transaction and
        --     a commit or rollback operation would generate an error.

    -- Test whether the transaction is uncommittable.
    IF (XACT_STATE()) = -1
    BEGIN
        PRINT
            N'The transaction is in an uncommittable state.' +
            'Rolling back transaction.'
        ROLLBACK TRANSACTION;
    END;

    -- Test whether the transaction is committable.
    IF (XACT_STATE()) = 1
    BEGIN
        PRINT
            N'The transaction is committable.' +
            'Committing transaction.'
        COMMIT TRANSACTION;   
    END;
END CATCH;
GO