@@ERROR の使用

@@ERROR システム関数は、最後の Transact-SQL ステートメントの実行が成功した場合は 0 を返し、エラーが発生した場合はエラー番号を返します。@@ERROR の値は、Transact-SQL ステートメントが完了するたびに変わります。

Transact-SQL ステートメントが完了するたびに @@ERROR は新しい値を取得するため、次のいずれかの方法で @@ERROR を処理します。

  • Transact-SQL ステートメントの直後で @@ERROR をテストまたは使用する。

  • Transact-SQL ステートメントが完了した直後に @@ERROR を整数変数に保存する。後でその変数値を使用できます。

エラーを生成するステートメントが TRY...CATCH 構造の TRY ブロックの中にない場合、エラーを生成したステートメントの直後のステートメントで、@@ERROR をテストまたは使用する必要があります。エラーを生成するステートメントが TRY ブロックの中にある場合、対応する CATCH ブロックの最初のステートメントで @@ERROR をテストまたは使用できます。CATCH ブロックのスコープの内部では、ERROR_NUMBER 関数を使用して、@@ERROR によって報告されたものと同じエラー番号を取得できます。@@ERROR は CATCH ブロック内の最初のステートメントによってリセットされますが、ERROR_NUMBER には、CATCH ブロックのスコープ内のすべてのステートメントに使用できるという利点があります。

IF ステートメントなどの条件ステートメントにより、@@ERROR がリセットされます。IF ステートメントで @@ERROR を参照している場合、IF ブロックまたは ELSE ブロック内の @@ERROR への参照では、@@ERROR 情報が取得されません。次の例では、IF によって @@ERROR がリセットされ、PRINT ステートメントで参照したときにエラー番号が返されません。

DECLARE @ErrorVar INT

RAISERROR(N'Message', 16, 1);
IF @@ERROR <> 0
    -- This PRINT statement prints 'Error = 0' because
    -- @@ERROR is reset in the IF statement above.
    PRINT N'Error = ' + CAST(@@ERROR AS NVARCHAR(8));
GO

次の例では、予想どおりの結果が返されます。

DECLARE @ErrorVar INT

RAISERROR(N'Message', 16, 1);
-- Save the error number before @@ERROR is reset by
-- the IF statement.
SET @ErrorVar = @@ERROR
IF @ErrorVar <> 0
-- This PRINT statement correctly prints 'Error = 50000'.
    PRINT N'Error = ' + CAST(@ErrorVar AS NVARCHAR(8));
GO

ステートメントの実行後に @@ERROR および @@ROWCOUNT の両方を参照する場合、同じステートメント内で参照する必要があります。Transact-SQL の各ステートメントで @@ERROR および @@ROWCOUNT の両方がリセットされるため、テストされるステートメントの直後の同じステートメントでその両方を参照する必要があります。次の例では、@@ROWCOUNT は最初の PRINT ステートメントによってリセットされるまでは参照されないため、@@ROWCOUNT は常に 0 です。

USE AdventureWorks2008R2;
GO
DELETE FROM HumanResources.JobCandidate
    WHERE JobCandidateID = 13;
-- This PRINT would successfully capture any error number.
PRINT N'Error = ' + CAST(@@ERROR AS NVARCHAR(8));
-- This PRINT will always print 'Rows Deleted = 0 because
-- the previous PRINT statement set @@ROWCOUNT to 0.
PRINT N'Rows Deleted = ' + CAST(@@ROWCOUNT AS NVARCHAR(8));
GO

次の例では、予想どおりの結果が返されます。

USE AdventureWorks2008R2;
GO
DECLARE @ErrorVar INT;
DECLARE @RowCountVar INT;

DELETE FROM HumanResources.JobCandidate
  WHERE JobCandidateID = 13;
-- Save @@ERROR and @@ROWCOUNT while they are both
-- still valid.
SELECT @ErrorVar = @@ERROR,
    @RowCountVar = @@ROWCOUNT;
IF (@ErrorVar <> 0)
    PRINT N'Error = ' + CAST(@ErrorVar AS NVARCHAR(8));
PRINT N'Rows Deleted = ' + CAST(@RowCountVar AS NVARCHAR(8));
GO

@@ERROR はエラーに対してだけ発生し、警告に対しては発生しません。したがって、バッチ、ストアド プロシージャ、およびトリガーでは、発生した警告を @@ERROR を使用して検出することはできません。

一般に、SQL Server 2000 以前の @@ERROR はストアド プロシージャが成功したか失敗したかを示すために使用されます。整数の変数は 0 に初期化されます。Transact-SQL ステートメントが完了するたびに @@ERROR が 0 であるかどうかがテストされ、0 以外の場合は変数に格納されます。次に、プロシージャは RETURN ステートメントで変数を返します。プロシージャ内のいずれの Transact-SQL ステートメントにもエラーが発生しなかった場合、変数は 0 のままになります。1 つ以上のステートメントでエラーが生成された場合、変数には最後のエラー番号が含まれます。このロジックを使った簡単なストアド プロシージャの例を次に示します。

USE AdventureWorks2008R2;
GO
IF EXISTS(SELECT name FROM sys.objects
          WHERE name = N'SampleProcedure')
    DROP PROCEDURE SampleProcedure;
GO
-- Create a procedure that takes one input parameter
-- and returns one output parameter and a return code.
CREATE PROCEDURE SampleProcedure @EmployeeIDParm INT,
    @MaxVacation INT OUTPUT
AS

    -- Declare and initialize a variable to hold @@ERROR.
    DECLARE @ErrorSave1 INT, @ErrorSave2 INT;
    SET @ErrorSave1 = 0;

    -- Do a SELECT using the input parameter.
    SELECT LoginID, NationalIDNumber, JobTitle
        FROM HumanResources.Employee
        WHERE BusinessEntityID = @EmployeeIDParm;

    -- Save @@ERROR value in first local variable.
    SET @ErrorSave1 = @@ERROR;

    -- Set a value in the output parameter.
    SELECT @MaxVacation = MAX(VacationHours)
        FROM HumanResources.Employee;

    -- Save @@ERROR value in second local variable. 
    SET @ErrorSave2 = @@ERROR;
    -- If second test variable contains non-zero value, 
    -- overwrite value in first local variable.
    IF (@ErrorSave2 <> 0) SET @ErrorSave1 = @ErrorSave2;

    -- Returns 0 if neither SELECT statement had
    -- an error; otherwise, returns the last error.
    RETURN @ErrorSave1;
GO
    
DECLARE @OutputParm INT;
DECLARE @ReturnCode INT;

EXEC @ReturnCode = SampleProcedure 13, @OutputParm OUTPUT;

PRINT N'OutputParm = ' + CAST(@OutputParm AS NVARCHAR(20));
PRINT N'ReturnCode = ' + CAST(@ReturnCode AS NVARCHAR(20));
GO

@@ERROR と TRY...CATCH

エラーを検出する主要な手段として @@ERROR を使用すると、TRY...CATCH 構造で使用されるコードとは非常に異なる形式のエラー処理コードになります。

  • どのステートメントでエラーが発生するのかを開発者は事前に予測できないため、各 Transact-SQL ステートメントの後で @@ERROR をテストまたは保存する必要があります。これにより、指定されたロジックの一部を実装するためにコードを作成する必要がある Transact-SQL ステートメントの数が倍になります。

  • TRY...CATCH 構造はより単純です。Transact-SQL ステートメントのブロックを BEGIN TRY ステートメントから END TRY ステートメントの間に囲んで、そのステートメントのブロックが生成したエラーを処理するために 1 つの CATCH ブロックを記述します。

CATCH ブロックの外部では、エラーを生成したバッチ、ストアド プロシージャ、またはトリガー内で使用できるデータベース エンジンのエラーとして使用できるのは @@ERROR だけです。オブジェクト名などの置換文字列を格納したメッセージ テキスト、重要度レベル、および状態など、エラーのそれ以外の部分はすべて、API のエラー処理メカニズムを使用して処理できるアプリケーションにだけ返されます。エラーが発生して CATCH ブロックが呼び出された場合、システム関数である ERROR_LINE、ERROR_MESSAGE、ERROR_PROCEDURE、ERROR_NUMBER、ERROR_SEVERITY、および ERROR_STATE を使用できます。