Cuando desarrollamos con T-SQL, necesitamos tomar en consideración situaciones en la cuales nuestro código puede fallar. SQL Server provee herramientas para el manejo de errores. Esta se mejoro con SQL Server 2005 (TRY/CATCH), sin embargo existen todavía algunos aspectos de T-SQL que siguen faltando. SQL Server 2008 no agrego ninguna mejora en esta área.
En este documento vamos a ver cómo podemos hacer el manejo de errores sin el constructor TRY/CATCH.
Recordemos que el manejo de errores en T-SQL sin TRY/CATCH tiene limitaciones. La principal herramienta que se tiene es la función @@ERROR, esta nos regresa un entero de la última sentencia ejecutada, donde cero indica que no hay error y diferente de cero el error que se genero. Siempre debemos asignar este valor a una variable sino queremos que se pierda el valor. Una vez teniendo el número de error debemos incluir en el código un manejo de errores y pasarle el control con un comando GOTO si queremos realizar alguna acción o simplemente imprimir el error generado.
Existen errores en el código que no van a mostrar el error y simplemente van a terminar el batch, por ejemplo el hacer referencia a un objeto que no exista o algún error al convertir algún tipo de dato. La forma correcta de asignar estas variables es la siguiente:
DECLARE @err AS INT, @rc AS INT;
--Sentencia SQL
SELECT @err = @@ERROR, @rc = @@ROWCOUNT;
--Manejo de errores en esta sección
SELECT @err AS error_number, @rc AS row_count;
Las variables no deben asignarse de la siguiente manera ya que no se asigna el valor correcto al segundo SET.
SET @err = @@ERROR;
SET @rc = @@ROWCOUNT;
A continuación se muestra un ejemplo de la manera en que debemos crear nuestros procedures y la forma en que debemos ejecutarlos para capturar los errores.
CREATE PROC dbo.prc_ejemplo
@id AS int,
@defecha AS DATETIME = '19000101',
@hoy AS DATETIME = '99991231 23:59:59.997',
@numrows AS INT OUTPUT
AS
DECLARE @err AS INT;
SELECT id, nombre FROM usuarios
WHERE id = @id
AND fechaAlta >= @defecha
AND fechaAlta < @hoy
SELECT @numrows = @@ROWCOUNT, @err = @@ERROR
RETURN @err
GO
Con este código el procedure va regresar en la variable @@ERROR el estatus de la ejecución y en @@ROWCOUNT la salida asignada por la variable @numrows, todo esto en caso de que el procedure no falle por algún error que no sea reportado en un batch como los comentados en la NOTA anterior, para ese caso lo debemos evaluar al ejecutar el procedure, como lo muestro en el siguiente ejemplo.
SET LOCK_TIMEOUT 5000
DECLARE @err AS INT, @rc AS INT
EXEC @err = dbo.prc_ejemplo
@id = 1,
@fromdate = '20100101',
@todate = '20100107',
@numrows = @rc OUTPUT
SELECT – Para errores del tipo batch-aborting
@err = COALESCE(@err, @@ERROR),
@rc = COALESCE(@rc, @@ROWCOUNT)
SELECT @err AS error_number, @rc AS row_count
IF @err = 0 AND @rc > 0 BEGIN
PRINT 'Ejecutado exitosamente'
RETURN
END
IF @err = 0 AND @rc = 0 BEGIN
PRINT 'No se seleccionaron registros.'
RETURN
END
IF @err = 1222
BEGIN
PRINT 'Expiro el tiempo de bloqueo.'
RETURN
END
-- Otros errores
-- IF @err = ...
BEGIN
PRINT 'Error desconocido encontrado.'
RETURN
END
En el código anterior en caso de presentarse un error que aborte el batch, será capturado con la variable @err, de lo contrario traerá el valor asignado por el procedure.
Es muy importante manejar el error de timeout o el tiempo que puede permanecer bloqueada una transacción, para poder tomar acciones dentro de nuestro código, ya sea ejecutar nuevamente la sentencia o informar al usuario del problema presentado mediante una pantalla de errores. De esa manera el usuario puede informar al área de soporte correspondiente del problema que se le presento.