使用 @@ERROR

如果上一个 Transact-SQL 语句执行成功,@@ERROR 系统函数将返回 0;如果该语句生成错误,@@ERROR 将返回错误号。每个 Transact-SQL 语句完成时,@@ERROR 的值都会更改。

因为每个 Transact-SQL 语句完成时,@@ERROR 都会获得一个新值,可以用以下两种方法之一处理 @@ERROR:

  • 在 Transact-SQL 语句完成后,立即测试或使用 @@ERROR。

  • 在 Transact-SQL 语句完成后,立即将 @@ERROR 保存到整数变量中。此变量的值可供以后使用。

如果生成错误的语句不在 TRY...CATCH 构造的 TRY 块中,则在生成错误的语句完成后,必须立即在该语句中测试或使用 @@ERROR。如果生成错误的语句位于 TRY 块中,则可以在关联 CATCH 块中的第一个语句中测试或使用 @@ERROR。在 CATCH 块的范围内,可以使用 ERROR_NUMBER 函数检索 @@ERROR 报告的同一错误号。ERROR_NUMBER 具有可用于 CATCH 块范围内的所有语句的优点,而 @@ERROR 可以通过 CATCH 块中的第一个语句重置。

条件性语句(如 IF 语句)可以重置 @@ERROR。如果在 IF 语句中引用 @@ERROR,则在 IF 或 ELSE 块中对 @@ERROR 的引用将不检索 @@ERROR 信息。在下面的示例中,@@ERROR 通过 IF 重置,并且在 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,则必须在同一语句中引用它们。@@ERROR 和 @@ROWCOUNT 都可以使用每条 Transact-SQL 语句进行重置;因此,在对其中一个进行测试后,必须在同一语句中同时引用它们。在下面的示例中,@@ROWCOUNT 将始终为 0,因为它在被第一个 PRINT 语句重置之后才被引用。

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。如果一个或多个语句生成错误,变量将保存最后一个错误号。下面的示例显示了具有此逻辑的简单存储过程。

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 语句绑定,然后编写一个 CATCH 块来处理该语句块可能生成的错误。

在 CATCH 块的外部,@@ERROR 仅是生成错误的批处理、存储过程或触发器中存在的数据库引擎错误的一部分。错误的所有其他部分 [如严重性、状态、包含替换字符串(例如对象名)的消息文本] 都仅返回到可以使用 API 错误处理机制对其进行处理的应用程序中。如果错误调用 CATCH 块,则可以使用系统函数 ERROR_LINE、ERROR_MESSAGE、ERROR_PROCEDURE、ERROR_NUMBER、ERROR_SEVERITY 和 ERROR_STATE。