Использование функции @@ERROR

Системная функция @@ERROR возвращает значение 0, если последняя инструкция Transact-SQL успешно завершена; если выполнение инструкции завершилось ошибкой, функция @@ERROR возвращает номер ошибки. Значение функции @@ERROR изменяется после выполнения каждой инструкции Transact-SQL.

Так как функция @@ERROR получает новое значение после завершения каждой инструкции Transact-SQL, можно обрабатывать @@ERROR двумя способами.

  • Проверять или использовать @@ERROR немедленно после инструкции Transact-SQL.

  • Сохранять значение функции @@ERROR в переменную типа integer сразу же после завершения инструкции Transact-SQL. Это значение переменной может быть использовано позднее.

Если инструкция, формирующая ошибку, находится вне блока TRY конструкции TRY...CATCH, то функция @@ERROR должна быть проверена или использована в инструкции, следующей сразу после инструкции, формирующей ошибку. Если инструкция, формирующая ошибку, находится в блоке TRY, то функция @@ERROR может быть проверена или использована в первой инструкции, в соответствующем блоке CATCH. В области блока CATCH можно использовать функцию ERROR_NUMBER, чтобы получить тот же номер ошибки, о котором сообщает функция @@ERROR. Преимущество функции ERROR_NUMBER состоит в том, что она доступна для всех инструкций в пределах блока CATCH, тогда как функция @@ERROR сбрасывается первой же инструкцией в блоке CATCH.

Условные инструкции, такие как инструкция IF, сбрасывают значение функции @@ERROR. Если инструкция IF содержит ссылку на функцию @@ERROR, то ссылка на функцию @@ERROR в блоке IF или ELSE не восстанавливает сведения о функции @@ERROR. В следующем примере инструкция IF сбрасывает функцию @@ERROR, и при обращении к функции @@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, они должны вызываться из одной инструкции. Значения обеих функции @@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 в основном применялась, чтобы определить, успешно ли завершилась хранимая процедура. Переменной типа integer присваивается значение 0. После выполнения каждой инструкции Transact-SQL проверяется значение @@ERROR, и если оно не равно значению 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.

  • Значение функции @@ERROR должно проверяться или сохраняться после каждой инструкции языка Transact-SQL, потому что разработчик не в состоянии предсказать, какая именно инструкция формирует ошибку. Это удваивает количество инструкций языка Transact-SQL, требуемых для реализации данного фрагмента логики.

  • Конструкции TRY…CATCH являются намного более простыми. Блок инструкций языка Transact-SQL ограничивается инструкциями BEGIN TRY и END TRY, за которыми следует один блок CATCH, обрабатывающий ошибки, которые могут возникнуть при выполнении блока инструкций, ограниченного инструкциями BEGIN TRY и END TRY.

Вне блока CATCH значение функции @@ERROR является только частью ошибки компонента Database Engine, доступной внутри пакета, хранимой процедуры или триггера, вызвавшего ошибку. Все остальные части ошибки, такие как серьезность, состояние и текст сообщения, содержащий строки замещения (например, названия объектов) возвращаются только в приложение, в котором их можно обработать с помощью API-механизмов обработки ошибок. Если ошибка вызывает блок CATCH, можно использовать системные функции ERROR_LINE, ERROR_MESSAGE, ERROR_PROCEDURE, ERROR_NUMBER, ERROR_SEVERITY и ERROR_STATE.