Usar @@ERROR

La función del sistema @@ERROR devuelve 0 si la última instrucción Transact-SQL se ejecutó con éxito; si la instrucción causó un error, @@ERROR devuelve el número de error. El valor de @@ERROR cambia al finalizar cada instrucción Transact-SQL.

Puesto que @@ERROR obtiene un nuevo valor cuando se completa cada instrucción Transact-SQL, procese @@ERROR de una de estas dos formas:

  • Pruebe o use @@ERROR inmediatamente después de la instrucción Transact-SQL.

  • Guarde @@ERROR en una variable de tipo entero inmediatamente después de que se complete la instrucción Transact-SQL. El valor de la variable se puede usar posteriormente.

Si la instrucción que genera el error no está en el bloque TRY de una construcción TRY…CATCH, @@ERROR debe probarse o utilizarse en la instrucción inmediatamente después de la instrucción que ha generado el error. Si la instrucción que genera el error está en un bloque TRY, @@ERROR se puede probar o utilizar en la primera instrucción del bloque CATCH asociado. En el ámbito de un bloque CATCH, la función ERROR_NUMBER se puede usar para recuperar el mismo número de error notificado por @@ERROR. ERROR_NUMBER tiene la ventaja de que está disponible para todas las instancias del ámbito del bloque CATCH, mientras que @@ERROR se restablece mediante la primera instrucción del bloque CATCH.

Las instrucciones condicionales, como la instrucción IF, restablecen @@ERROR. Si hace referencia a @@ERROR en una instrucción IF, las referencias a @@ERROR en los bloques IF o ELSE no recuperarán la información de @@ERROR. En el siguiente ejemplo, @@ERROR ha sido restablecido por IF y no devuelve el número de error cuando se le hace referencia en la instrucción 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

En el siguiente ejemplo se devuelven los resultados esperados.

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

Si desea hacer referencia a @@ERROR y @@ROWCOUNT después de ejecutar una instrucción, deben tener una referencia en la misma instrucción. @@ERROR y @@ROWCOUNT se restablecen con cada instrucción Transact-SQL; por lo tanto, debe hacerse referencia a ambas en la misma instrucción inmediatamente después de la que se prueba. En el siguiente ejemplo, @@ROWCOUNT siempre será 0 porque no se hace referencia a ella hasta que se restablece mediante la primera instrucción 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

En el siguiente ejemplo se devuelven los resultados esperados.

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 se genera sólo para errores, no para advertencias; los lotes, procedimientos almacenados y desencadenadores no pueden usar @@ERROR para detectar advertencias que se hayan producido.

Una de las utilizaciones más comunes de @@ERROR en SQL Server 2000 y versiones anteriores es indicar si un procedimiento almacenado se ha ejecutado correctamente o no. Una variable entera se inicializa a 0. Una vez completada cada instrucción de Transact-SQL, se comprueba que @@ERROR sea 0 y, si no lo es, se almacena en la variable. A continuación, el procedimiento devuelve la variable en la instrucción RETURN. Si ninguna de las instrucciones de Transact-SQL del procedimiento tuvo un error, la variable sigue siendo 0. Si una o más instrucciones generaron un error, la variable contiene el último número de error. En el siguiente ejemplo se muestra un procedimiento almacenado simple con esta lógica.

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 frente a TRY...CATCH

El uso de @@ERROR como medio principal de detección de errores conduce a un código de gestión de errores de estilo muy diferente del que se utiliza con construcciones TRY…CATCH.

  • @@ERROR debe comprobarse o guardarse después de cada instrucción de Transact-SQL porque un programador no puede predecir qué instrucción puede generar un error. Esto dobla el número de instrucciones de Transact-SQL que deben codificarse para implementar un fragmento de lógica dado.

  • Las construcciones TRY…CATCH son mucho más simples. Un bloque de instrucciones de Transact-SQL está delimitado por instrucciones BEGIN TRY y END TRY, y después se escribe un bloque CATCH para administrar los errores que pueden generarse en el bloque de instrucciones.

Fuera de un bloque CATCH, @@ERROR es la única parte de un error de Motor de base de datos disponible en el lote, procedimiento almacenado o desencadenador que ha generado el error. El resto de las partes del error, como su gravedad, estado y texto del mensaje que contienen cadenas de sustitución (nombres de objeto, por ejemplo) sólo se devuelven a la aplicación, donde pueden procesarse mediante los mecanismos de control de errores de las API. Si el error invoca un bloque CATCH, se pueden usar las funciones del sistema ERROR_LINE, ERROR_MESSAGE, ERROR_PROCEDURE, ERROR_NUMBER, ERROR_SEVERITY y ERROR_STATE.