Transacciones: BEGIN, COMMIT y ROLLBACK
Imagina que estás transfiriendo dinero de una cuenta bancaria a otra. Son dos operaciones: restar de una cuenta y sumar a la otra. ¿Qué pasa si la primera se ejecuta pero la segunda falla? Tendrías dinero que desaparece. Las transacciones existen para evitar exactamente eso.
INSERT INTO cuentas ...UPDATE cuentas SET ...DELETE FROM logs ...¿Qué es una transacción?
Una transacción es un grupo de operaciones SQL que se ejecutan como una unidad indivisible. O se ejecutan todas correctamente, o no se ejecuta ninguna.
Este principio se conoce como atomicidad, y es la primera letra de las propiedades ACID que garantizan la fiabilidad de las bases de datos:
| Propiedad | Significado |
|---|---|
| Atomicidad | Todo o nada. Si una operación falla, se deshacen todas. |
| Consistencia | La base de datos pasa de un estado válido a otro estado válido. |
| Isolamiento | Las transacciones concurrentes no se interfieren entre sí. |
| Durabilidad | Una vez confirmada, la transacción sobrevive a fallos del sistema. |
Sintaxis básica
BEGIN TRANSACTION;
-- Operaciones que forman la transacción
UPDATE cuentas SET saldo = saldo - 100 WHERE id = 1;
UPDATE cuentas SET saldo = saldo + 100 WHERE id = 2;
COMMIT;BEGIN TRANSACTION (o simplemente BEGIN) abre la transacción. Todas las operaciones que escribas después quedan en espera hasta que las confirmes con COMMIT o las deshagas con ROLLBACK.
COMMIT: confirmar los cambios
COMMIT guarda permanentemente todos los cambios realizados desde el BEGIN. Una vez ejecutado, no hay vuelta atrás.
BEGIN TRANSACTION;
INSERT INTO pedidos (cliente_id, total) VALUES (5, 59.99);
UPDATE clientes SET total_compras = total_compras + 59.99 WHERE id = 5;
COMMIT;Si ambas operaciones se ejecutan sin error, COMMIT las graba definitivamente.
ROLLBACK: deshacer los cambios
ROLLBACK deshace todos los cambios realizados desde el BEGIN. La base de datos vuelve exactamente al estado anterior a la transacción.
BEGIN TRANSACTION;
DELETE FROM videogames WHERE developer = 'Valve';
-- ¡Ups! No queríamos borrar todos los juegos de Valve
ROLLBACK;
-- La tabla queda como estaba antes del DELETEEsto es extremadamente útil como red de seguridad cuando ejecutas operaciones peligrosas.
Comportamiento por defecto: autocommit
Sin una transacción explícita, cada sentencia SQL se ejecuta en su propia transacción implícita que se confirma automáticamente. Esto se llama autocommit.
-- Esto se confirma automáticamente
DELETE FROM videogames WHERE id = 1;
-- El registro ya se borró permanentemente, no hay ROLLBACK posibleCuando usas BEGIN, desactivas el autocommit hasta que ejecutes COMMIT o ROLLBACK.
Ejemplo clásico: transferencia bancaria
Este es el ejemplo más importante para entender por qué las transacciones existen:
BEGIN TRANSACTION;
-- Paso 1: Restar de la cuenta origen
UPDATE cuentas SET saldo = saldo - 500
WHERE id = 1 AND saldo >= 500;
-- Paso 2: Sumar a la cuenta destino
UPDATE cuentas SET saldo = saldo + 500
WHERE id = 2;
COMMIT;Sin transacción, si el sistema falla entre el paso 1 y el paso 2, el dinero desaparece. Con transacción, si algo falla, ROLLBACK asegura que ninguno de los dos cambios se aplica.
Transacciones en SQLite
SQLite soporta transacciones completamente, con algunas particularidades:
- Puedes usar
BEGIN TRANSACTIONo simplementeBEGIN. - SQLite también acepta
ENDcomo sinónimo deCOMMIT. - SQLite usa bloqueo a nivel de archivo: solo una escritura a la vez (por eso no es ideal para alta concurrencia).
-- Las tres formas son equivalentes en SQLite:
BEGIN TRANSACTION;
BEGIN;
-- Y para confirmar:
COMMIT;
END;Cuándo usar transacciones
Usa transacciones siempre que:
- Varias operaciones deben ser atómicas: transferencias, creación de entidades con relaciones, etc.
- Necesitas una red de seguridad: antes de ejecutar un
UPDATEoDELETEmasivo, envuélvelo en una transacción para poder hacerROLLBACKsi el resultado no es el esperado. - Rendimiento en inserciones masivas: envolver muchos
INSERTen una sola transacción es mucho más rápido que ejecutarlos individualmente, porque SQLite solo escribe al disco una vez en lugar de una vez por cadaINSERT.
-- Lento: cada INSERT es una transacción independiente
INSERT INTO logs VALUES (1, 'evento1');
INSERT INTO logs VALUES (2, 'evento2');
INSERT INTO logs VALUES (3, 'evento3');
-- ...miles más
-- Rápido: una sola transacción para todos
BEGIN TRANSACTION;
INSERT INTO logs VALUES (1, 'evento1');
INSERT INTO logs VALUES (2, 'evento2');
INSERT INTO logs VALUES (3, 'evento3');
-- ...miles más
COMMIT;La diferencia de rendimiento puede ser de 100x o más en SQLite.
Nuestras tablas
| id | titular | saldo |
|---|---|---|
| 1 | Ana García | 1500 |
| 2 | Luis Pérez | 800 |
| 3 | Marta López | 2300 |
| id | cuenta_id | tipo | cantidad |
|---|---|---|---|
| 1 | 1 | ingreso | 1500 |
| 2 | 2 | ingreso | 800 |
| 3 | 3 | ingreso | 2300 |