¡Hola, chicos!
¡Primer post de 2019!

En este artículo explicaré la diferencia entre @@ERROR y la función ERROR_NUMBER(), que en teoría trae el mismo resultado, es decir, la cantidad de errores causados ​​por alguna operación en la sesión actual. La idea de escribir sobre esto surgió a partir de una pregunta de un nigeriano en el grupo Servidor SQL – DBA, de Telegram y pensé que sería genial publicar esta explicación en mi blog para ayudar a otras personas con la misma pregunta.

Primero, consultemos la documentación oficial de Microsoft para ver si tenemos alguna pista sobre la diferencia entre estos 2 métodos:

@@ERROR

Devuelve el número de error de la última instrucción Transact-SQL ejecutada. Devuelve 0 si la instrucción Transact-SQL anterior no encontró ningún error.

Dado que @@ERROR se borra y restablece en cada declaración ejecutada, consúltelo inmediatamente después de verificar la declaración o guárdelo en una variable local que pueda verificarse más tarde.

NÚMERO_ERROR

Esta función devuelve el número de error que provocó la ejecución del bloque CATCH de una construcción TRY…CATCH. Cuando se llama en un bloque CATCH, ERROR_NUMBER devuelve el número del error que provocó la ejecución del bloque CATCH. ERROR_NUMBER devuelve NULL cuando se llama fuera del alcance de un bloque CATCH.

ERROR_NUMBER devuelve un número de error relevante independientemente de cuántas veces o dónde se ejecuta dentro del alcance del bloque CATCH. Esto es diferente de una función como @@ERROR, que solo devuelve un número de error en la declaración inmediatamente posterior a la que causa el error.

En un bloque CATCH anidado, ERROR_NUMBER devuelve el número del error específico del alcance del bloque CATCH que hace referencia a este bloque CATCH. Por ejemplo, el bloque CATCH de una construcción TRY…CATCH externa podría tener una construcción TRY…CATCH interna. Dentro de este bloque CATCH interno, ERROR_NUMBER devuelve el número del error que invocó el bloque CATCH interno. Si se ejecuta ERROR_NUMBER en el bloque CATCH externo, devuelve el número del error que invocó ese bloque CATCH externo.

Analizando la documentación, queda clara la diferencia entre estos 2 métodos para identificar el código de error:

  • @@ERROR: Devuelve el código de error de la última instrucción ejecutada (con cada nueva instrucción, la variable se restablece a cero). Devuelve 0 si no hay error o si la variable es cero. Se recomienda asignar el resultado @@ERROR a una variable local justo al comienzo de CATCH, ya que CUALQUIER COMANDO hace que @@ERROR se restablezca (incluso un SELECT @@ERROR).
  • NÚMERO_ERROR: Devuelve el código de error dentro del alcance de un bloque CATCH. Devuelve NULL si intenta utilizar el comando fuera de un bloque CATCH

Y ahora, ¡veamos cómo funciona esto en la práctica!

BEGIN TRY
    SELECT 1/0
END TRY
BEGIN CATCH
    
    -- Os 2 comandos vão trazer o mesmo retorno
    SELECT ERROR_NUMBER() AS [ERROR_NUMBER_1], @@ERROR AS [@@ERROR_1]

    BEGIN TRY
        EXEC('SELECT * FROM dbo.Teste_123')
    END TRY

    BEGIN CATCH
        
        /*

        Aqui, vou colocar um comando PRINT para "atrapalhar" o comportamento do @@ERROR
        Como o @@ERROR é resetado a cada comando executado, o PRINT vai fazer com que a variável @@ERROR seja zerada

        */

        PRINT 'Esse print vai atrapalhar o @@ERROR'
        SELECT ERROR_NUMBER() AS [ERROR_NUMBER_2], @@ERROR AS [@@ERROR_2]

        BEGIN TRY
            EXEC dbo.stpTeste    
        END TRY

        BEGIN CATCH

            -- Aqui nada de novo também.. os 2 terão o mesmo retorno
            SELECT ERROR_NUMBER() AS [ERROR_NUMBER_3], @@ERROR AS [@@ERROR_3]

            -- Mas se eu tentar executar novamente o comando... o ERROR_NUMBER permanece igual, enquanto o @@ERROR já foi zerado
            SELECT ERROR_NUMBER() AS [ERROR_NUMBER_3_DENOVO], @@ERROR AS [@@ERROR_3_DENOVO]

        END CATCH

    END CATCH

    /*
    
    Já aqui, o ERROR_NUMBER vai retornar a mensagem de erro do bloco 1, que é o escopo atual do bloco CATCH
    Já a variável @@ERROR ficará vazia, pois outros comandos foram executados depois da última mensagem de erro

    */

    SELECT ERROR_NUMBER() AS [ERROR_NUMBER_VOLTOU_PRO_1], @@ERROR AS [@@ERROR_ZERADO_PORQUE_OUTRO_COMANDO_JA_FOI_EXECUTADO]

END CATCH


-- Por fim, como o ERROR_NUMBER() não está dentro de nenhum bloco de CATCH com erro, retornará NULL e a @@ERROR retornará 0
SELECT ERROR_NUMBER() AS [ERROR_NUMBER_FINAL], @@ERROR AS [@@ERROR_FINAL]

Resultado:

Pero, ¿qué pasa si no estoy usando TRY..CATCH y quiero capturar el código de error? ¿Puedo utilizar cualquiera de los dos métodos?

Respuesta: ¡NO! sí No está dentro de un bloque TRY..CATCH, la función ERROR_NUMBER() devolverá NULL, mientras que la variable @@ERROR devolverá el código de error (recuerda las condiciones de uso de @@ERROR para evitar que se resetee y pierdas esta información).

¡Y eso es todo, amigos!
Publicación muy breve y objetiva y espero que haya ayudado a aclarar la diferencia entre @@ERROR y ERROR_NUMBER().
Y recuerda: UTILIZA INTENTAR... ¡ATRAPAR!

¡Que tengas un buen 2019 y nos vemos en el próximo artículo!
¡Gran abrazo!