Transacciones y TRY/CATCH: completar o revertir una unidad de trabajo
Cuando una operación modifica varias tablas, conservar solo la primera mitad puede dejar datos inconsistentes. Una transacción agrupa instrucciones para confirmarlas juntas con COMMIT o deshacerlas con ROLLBACK.
TRY/CATCH permite capturar errores y asegurar que una transacción pendiente no quede abierta silenciosamente.
La idea
BEGIN TRANSACTIONinicia la unidad de trabajo.COMMITconfirma los cambios.ROLLBACKrevierte los cambios todavía no confirmados.TRY/CATCHsepara la ejecución normal del manejo de errores.
Sintaxis
BEGIN TRY
BEGIN TRANSACTION;
-- Instrucciones relacionadas.
COMMIT;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK;
THROW;
END CATCH;
THROW vuelve a entregar el error al proceso que ejecutó el código. Ocultarlo podría hacer creer a la aplicación que la operación terminó correctamente.
Ejemplo: crear un pedido y su partida
El ejemplo también utiliza tres construcciones auxiliares:
SET XACT_ABORT ONindica que ciertos errores de ejecución deben terminar y revertir la transacción completa.SYSDATETIME()obtiene la fecha y hora actuales del servidor.SCOPE_IDENTITY()devuelve el último valorIDENTITYgenerado en el mismo alcance. El alcance es el lote, procedimiento o trigger donde se ejecutó la inserción.
SET XACT_ABORT ON;
BEGIN TRY
BEGIN TRANSACTION;
INSERT INTO dbo.Pedido
(IdCliente, FechaPedido, Estado)
VALUES
(3, SYSDATETIME(), 'Pendiente');
DECLARE @IdPedido BIGINT = SCOPE_IDENTITY();
INSERT INTO dbo.PedidoDetalle
(IdPedido, NumeroPartida, IdProducto,
Cantidad, PrecioUnitario, Descuento)
VALUES
(@IdPedido, 1, 1, 2, 349.00, 0);
COMMIT;
END TRY
BEGIN CATCH
IF XACT_STATE() <> 0
ROLLBACK;
THROW;
END CATCH;
Si la partida no puede insertarse, el bloque CATCH revierte también el encabezado del pedido. SET XACT_ABORT ON hace que muchos errores de ejecución terminen la transacción en lugar de permitir que el lote continúe en un estado parcial.
XACT_STATE() informa el estado de la transacción actual: devuelve 1 cuando todavía puede confirmarse, 0 cuando no existe una transacción activa y -1 cuando solo puede revertirse. Dentro del CATCH, la condición <> 0 cubre los dos estados donde todavía existe una transacción.
Transacciones anidadas
Llamar varias veces a BEGIN TRANSACTION incrementa @@TRANCOUNT, pero no crea unidades independientes que puedan confirmarse por separado. Un ROLLBACK sin punto de guardado revierte la transacción completa.
Cuando un procedimiento puede ejecutarse dentro de otra transacción, debe acordarse quién es responsable de iniciarla, confirmarla y revertirla. Un patrón aislado no sustituye ese contrato.
Mantener la transacción corta
Mientras una transacción permanece abierta puede conservar bloqueos —protecciones que coordinan el acceso concurrente—, aumentar el uso del log de transacciones, donde SQL Server registra los cambios, y hacer esperar a otras sesiones. Las validaciones que no necesitan protegerse dentro de la misma unidad deberían realizarse antes; la interacción humana nunca debe mantener abierta una transacción de producción.
Qué conviene comprobar
Una transacción garantiza atomicidad: todas las operaciones incluidas se confirman o ninguna permanece. No valida la lógica del negocio. Todavía necesitamos filtros correctos, restricciones, manejo de concurrencia y comprobación de filas afectadas.
También revisamos el nivel de aislamiento, las reglas que determinan qué cambios de otras transacciones puede observar una sesión, y los posibles bloqueos. Ampliar una transacción para sentir mayor seguridad puede producir el efecto contrario si aumenta innecesariamente la contención: varias sesiones esperando por los mismos recursos.