Contenido del curso
Contenido del curso
jefred bedoya
Harry Uran Molina
José Bernardo Navas Pleitez
Pablo Núñez
Rubens A. Rangel Gomez
Seguridad en Transacciones: TRY y CATCH
El manejo de errores es la red de seguridad indispensable para evitar transacciones abiertas que bloqueen recursos o dejen datos inconsistentes.
ROLLBACK y registrar el fallo para auditoría.Componentes Críticos del Manejo de Errores
CATCH, se capturan metadatos esenciales:
ERROR_NUMBER(): Identificador del error.ERROR_MESSAGE(): Descripción detallada.ERROR_LINE(): Ubicación exacta del fallo.ERROR_SEVERITY(): Nivel de gravedad del problema.Buenas Prácticas para Producción
BEGIN TRY...BEGIN TRANSACTION...COMMIT...CATCH...ROLLBACK asegura que el procedimiento sea robusto y reversible.¿Cuándo debo ejecutar un ROLLBACK exactamente?
Debes ejecutar la instrucción ROLLBACK TRANSACTION exclusivamente dentro del bloque CATCH. Su propósito principal es actuar como un botón de deshacer automático cuando algo sale mal dentro de tu bloque TRY. Si una inserción, actualización o eliminación falla por cualquier motivo (como una violación de llave primaria, un tipo de dato incorrecto o una desconexión), el flujo del programa salta inmediatamente al CATCH. Es en este punto exacto donde el ROLLBACK entra en acción para revertir cualquier modificación parcial que la transacción haya intentado aplicar en la base de datos. De esta manera, la información regresa a su estado original y seguro, garantizando que no queden registros huérfanos, saldos inconsistentes o datos a medio procesar.
Compato resultados de try y catch
USE TiendaLatam; GO CREATE OR ALTER PROCEDURE SP_CierreDeMes ( @Anio INT, @Mes INT, @CodigoPais NVARCHAR(2) = NULL, @Resultado NVARCHAR(20) OUTPUT ) AS BEGIN SET NOCOUNT ON; DECLARE @FilasInsertadas INT = 0; BEGIN TRY -- VALIDAR PARAMETROS IF @Anio < 2000 OR @Anio > YEAR(GETDATE()) + 1 OR @Mes < 1 OR @Mes > 12 BEGIN THROW 50001, 'Parámetros de fecha inválidos.', 1; END; -- VALIDAR SI YA FUE CERRADO IF EXISTS ( SELECT 1 FROM ResumenMensual WHERE (@CodigoPais IS NULL OR CodigoPais = @CodigoPais) AND Anio = @Anio AND Mes = @Mes ) BEGIN THROW 50002, 'El período ya fue cerrado anteriormente.', 1; END; -- INICIAR TRANSACCION BEGIN TRANSACTION; -- INSERTAR RESUMEN MENSUAL INSERT INTO ResumenMensual ( CodigoPais, Anio, Mes, TotalPedidos, VentasTotales, TicketPromedio ) SELECT pa.CodigoPais, @Anio, @Mes, COUNT(p.PedidoID), SUM(p.Total), AVG(p.Total) FROM Pedidos p INNER JOIN Clientes c ON p.ClienteID = c.ClienteID INNER JOIN Paises pa ON c.PaisID = pa.PaisID WHERE YEAR(p.FechaPedido) = @Anio AND MONTH(p.FechaPedido) = @Mes AND p.Estado = 'Completado' AND (@CodigoPais IS NULL OR pa.CodigoPais = @CodigoPais) GROUP BY pa.CodigoPais; -- GUARDAR FILAS INSERTADAS SET @FilasInsertadas = @@ROWCOUNT; -- INSERTAR LOG DE EXITO INSERT INTO LogCierres ( CodigoPais, Anio, Mes, Estado, Mensaje ) VALUES ( ISNULL(@CodigoPais,'ALL'), @Anio, @Mes, 'SUCCESS', 'Cierre exitoso. Países procesados: ' + CAST(@FilasInsertadas AS NVARCHAR(20)) ); DECLARE @Division INT; SET @Division = 10 / 0; -- CONFIRMAR TRANSACCION COMMIT TRANSACTION; SET @Resultado = 'SUCCESS'; END TRY BEGIN CATCH -- ROLLBACK SI HAY TRANSACCION ACTIVA IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION; -- DEFINIR RESULTADO SET @Resultado = CASE ERROR_NUMBER() WHEN 50002 THEN 'ALREADY_CLOSED' ELSE 'ERROR' END; -- INSERTAR LOG DE ERROR INSERT INTO LogCierres ( CodigoPais, Anio, Mes, Estado, Mensaje ) VALUES ( ISNULL(@CodigoPais,'ALL'), @Anio, @Mes, @Resultado, 'Error ' + CAST(ERROR_NUMBER() AS NVARCHAR(20)) + ': ' + ERROR_MESSAGE() ); -- RELANZAR ERRORES NO CONTROLADOS IF ERROR_NUMBER() NOT IN (50001, 50002) THROW; END CATCH; END; GO -- cierre DECLARE @Resultado NVARCHAR(20); EXEC SP_CierreDeMes @Anio = 2024, @Mes = 1, @Resultado = @Resultado OUTPUT; SELECT @Resultado as Resultado; SELECT * FROM ResumenMensual ORDER BY Anio, Mes, CodigoPais; SELECT * FROM LogCierres ORDER BY FechaHora DESC;
observación en el ejemplo final, nada más faltó ejecutar la línea declare, por lo que hubo un error de falta de creación de variable. me parece muy buena práctica utilizar la condicional ir @@trancount > 0 rollback transacción, . ya que el catch podría atrapar algún error que no sea específico antes de iniciado la transacción por lo que sería un problema realizar un rollback por un error anterior al inicio de la transacción
El manejo de errores con try-catch en SQL Server resulta más intuitivo porque es muy parecido al que se usa en lenguajes de programación tradicionales, lo que facilita escribir procedimientos almacenados más seguros y fáciles de mantener. En comparación, MySQL utiliza DECLARE HANDLER, que cumple la misma función pero con una sintaxis menos clara y más procedural. Por otro lado, PostgreSQL ofrece un enfoque muy robusto mediante bloques EXCEPTION, que conceptualmente se asemejan mucho al try-catch y permiten un control muy preciso de los errores. En todos los casos, el objetivo es el mismo: proteger la integridad de los datos, evitar transacciones incompletas y asegurar que cualquier fallo pueda revertirse correctamente.
SQL Server se parece más a un lenguaje de programación tradicional que MySQL y en algunos aspectos incluso más que PostgreSQL, porque T-SQL (Transact-SQL) tiene muchas estructuras típicas de programación.