TRY…CATCH es una estructura de control en T-SQL que permite manejar errores que pueden ocurrir en la ejecución de código.
Hasta ahora, si uno de nuestros scripts fallaba (por ejemplo, una división por cero o una violación de llave foránea), SQL Server mostraba un mensaje rojo y detenía la ejecución.
Pero eso… no queda muy profesional, no. No esta bien que dejemos que el código “explote” sin control. Necesitamos capturar el error, limpiarlo y decidir qué hacer.
Para ello, T-SQL implementa la estructura TRY...CATCH (muy similar a la que existe en lenguajes como C#, JavaScript o Python).
Estructura básica
Su funcionamiento es sencillo:
- BEGIN TRY: “Intenta hacer esto”.
- BEGIN CATCH: “Si algo falla arriba, salta aquí inmediatamente y ejecuta este plan de contingencia”.
Es decir, la sintaxis básica es la siguiente:
BEGIN TRY
-- Código que podría generar un error
END TRY
BEGIN CATCH
-- Manejo del error
END CATCH;
Ejemplo básico
Imaginemos que tenemos una tabla Productos y queremos insertar un nuevo producto. Si el producto ya existe (incumple una restricción UNIQUE), queremos manejar el error de manera controlada:
BEGIN TRY
INSERT INTO Productos (ID, Nombre, Precio)
VALUES (1, 'Laptop', 1200);
END TRY
BEGIN CATCH
PRINT 'Error: No se pudo insertar el producto.';
PRINT 'Mensaje de error: ' + ERROR_MESSAGE();
END CATCH;
En este ejemplo, si el producto con ID = 1 ya existe, se captura el error en el bloque CATCH y se muestra un mensaje informativo.
Obtener información del error
Saber que hubo un error está bien, pero también necesitamos saber qué pasó. Dentro del bloque CATCH, tenemos acceso a funciones especiales del sistema que nos dan los detalles del accidente:
| Función | Descripción |
|---|---|
ERROR_NUMBER() | El código interno del error. |
ERROR_MESSAGE() | El texto descriptivo (“Divide by zero error encountered”). |
ERROR_SEVERITY() | La gravedad (1-25). |
ERROR_LINE() | En qué línea de código explotó. |
ERROR_PROCEDURE() | En qué procedimiento almacenado ocurrió. |
Por ejemplo,
BEGIN TRY
-- Intentamos insertar un duplicado en una PK
INSERT INTO Clientes (ClienteID, Nombre) VALUES (1, 'Juan');
INSERT INTO Clientes (ClienteID, Nombre) VALUES (1, 'Pedro'); -- Falla aquí
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS NumeroError,
ERROR_MESSAGE() AS Mensaje,
ERROR_LINE() AS Linea;
END CATCH
Manejo de errores en transacciones
Uno de los usos más comunes de TRY...CATCH es proteger la integridad de los datos cuando usamos transacciones.
Si abres una transacción, haces dos cambios, y el tercero falla… ¿qué pasa con la transacción abierta? Se queda “colgada”, bloqueando tablas y consumiendo recursos.
Debemos usar el bloque CATCH para hacer el ROLLBACK (deshacer) automáticamente si algo sale mal.
BEGIN TRY
BEGIN TRANSACTION; -- Abrimos transacción
-- Paso 1: Restar Stock
UPDATE Productos SET Stock = Stock - 1 WHERE ID = 100;
-- Paso 2: Generar error provocado (ej: restricción CHECK)
UPDATE Ventas SET Cantidad = -5 WHERE ID = 999;
COMMIT TRANSACTION; -- Si todo va bien, guardamos
PRINT 'Venta finalizada con éxito';
END TRY
BEGIN CATCH
-- ¡Algo falló!
-- Verificamos si hay una transacción abierta para deshacerla
IF @@TRANCOUNT > 0
BEGIN
ROLLBACK TRANSACTION;
PRINT 'Error detectado. Se han deshecho todos los cambios.';
END
-- Mostramos el error original
SELECT ERROR_MESSAGE() AS MotivoFallo;
END CATCH
Este es el snippet (plantilla) que deberías usar siempre que escribas procedimientos almacenados transaccionales.
Relanzar el error: THROW
Cuando capturas un error con CATCH, para SQL Server el error ya ha sido manejado. El script termina “con éxito” (código de salida 0) aunque tú hayas impreso un mensaje de error.
Esto es peligroso porque la aplicación que llamó al SQL (tu backend en .NET, Java, etc.) pensará que todo fue bien.
Para avisar al mundo exterior de que hubo un problema, debemos relanzar el error usando THROW.
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
-- Registramos el error en una tabla de log (opcional)
INSERT INTO LogErrores (Mensaje) VALUES (ERROR_MESSAGE());
-- ¡Lanzamos el error de nuevo a la aplicación!
-- Sintaxis: THROW [Numero], [Mensaje], [Estado];
-- O simplemente ;THROW para relanzar el original.
;THROW;
END CATCH
Es una peculiaridad sintáctica famosa. La instrucción anterior al THROW debe terminar obligatoriamente con punto y coma ;.
Por costumbre, muchos desarrolladores escriben ;THROW para asegurarse.
