Seguro que muchos habéis oído hablar de las transacciones SQL. Sus usos, sus ventajas, sus inconvenientes y toda la teoría relacionada están bastante bien documentados.
Así que, en lugar de volver a explicar lo mismo, vamos directamente a un caso práctico y sencillo para entender cuándo pueden resultar realmente útiles.
El problema
Supongamos que tenemos dos tablas en nuestra base de datos:
- Una tabla de categorías.
- Una tabla de productos.
En nuestro backend mostramos un listado de productos junto con la categoría a la que pertenece cada uno.
Hasta aquí, ningún misterio.
Pero imaginemos que necesitamos cambiar la categoría de más de 100 productos.
¿Vamos producto por producto?
Y todavía podemos complicarlo un poco más: queremos modificar también el nombre de la categoría y necesitamos que todos los productos asociados continúen apuntando correctamente a ella.
Es decir, necesitamos realizar varias operaciones relacionadas y queremos que todas se completen correctamente o que no se aplique ninguna.
Ahí es donde entran en juego las transacciones.
La idea fundamental
Una transacción nos permite agrupar varias operaciones de base de datos y tratarlas como una única unidad de trabajo. Si todo sale bien, hacemos COMMIT. Si algo falla, hacemos ROLLBACK y deshacemos los cambios realizados durante la transacción.
En nuestro caso, necesitamos actualizar las dos tablas dentro de la misma operación para evitar que la base de datos quede en un estado intermedio si algo falla.

Y ahora que tenemos claro el escenario, vamos a ponerlo en práctica.
La transacción con PDO
Si estamos utilizando PDO, podemos iniciar una transacción mediante:
$query->beginTransaction();
A partir de ese momento, las operaciones que realicemos formarán parte de la transacción.
Si todo termina correctamente:
$query->commit();
Si se produce algún problema:
$query->rollBack();
Vamos a verlo con nuestro ejemplo.
Ejemplo completo
try {
$query = $this->dbh;
// Comenzamos la transacción
$query->beginTransaction();
// Actualizamos la categoría
$sql = "
UPDATE tipoproducto
SET tipoProducto = :tipoProducto,
imgCategoria = :imgCategoria
WHERE idTipo = :idTipo
";
$stmt = $query->prepare($sql);
$stmt->execute([
':idTipo' => $idTipo,
':tipoProducto' => $tipoProducto,
':imgCategoria' => $imgCategoria
]);
// Actualizamos los productos asociados
$sql = "
UPDATE productos
SET tipoProducto = :tipoProducto
WHERE idTipo = :idTipo
";
$stmt = $query->prepare($sql);
$stmt->execute([
':idTipo' => $idTipo,
':tipoProducto' => $tipoProducto
]);
// Si todo ha ido bien, confirmamos los cambios
$query->commit();
} catch (Exception $e) {
// Si algo ha fallado, deshacemos todos los cambios
if ($query->inTransaction()) {
$query->rollBack();
}
// Aquí podemos registrar el error en un log
// error_log($e->getMessage());
}
¿Qué está ocurriendo aquí?
El proceso es bastante sencillo.
Primero iniciamos la transacción:
$query->beginTransaction();
A continuación actualizamos la primera tabla:
UPDATE tipoproducto
SET tipoProducto = :tipoProducto,
imgCategoria = :imgCategoria
WHERE idTipo = :idTipo
Después actualizamos los productos relacionados:
UPDATE productos SET tipoProducto = :tipoProducto WHERE idTipo = :idTipo
Y finalmente, si ninguna operación ha producido una excepción, confirmamos los cambios:
$query->commit();
Si alguna de las operaciones falla, entramos en el bloque catch y ejecutamos:
$query->rollBack();
De esta forma, los cambios realizados durante la transacción se deshacen.
Importante: una transacción no hace magia
Para que este mecanismo funcione correctamente, las tablas y el motor de almacenamiento deben soportar transacciones. En MySQL y MariaDB, por ejemplo, InnoDB es el motor habitual cuando necesitamos este comportamiento.
¿Por qué utilizamos parámetros en la consulta?
Hay otro detalle importante en el código anterior.
En el código original las variables se concatenaban directamente dentro de las consultas SQL:
$sql = "UPDATE productos SET tipoProducto = '$tipoProducto' WHERE idTipo = '$idTipo'";
Aunque posteriormente se utilizara prepare(), la consulta ya había sido construida mediante concatenación.
Es mejor utilizar sentencias preparadas y parámetros desde el principio:
$sql = "
UPDATE productos
SET tipoProducto = :tipoProducto
WHERE idTipo = :idTipo
";
$stmt = $query->prepare($sql);
$stmt->execute([
':tipoProducto' => $tipoProducto,
':idTipo' => $idTipo
]);
Además de hacer el código más limpio, evitamos construir consultas mediante concatenación de valores proporcionados por variables.
Una buena costumbre
Si estamos utilizando PDO, acostumbrémonos a utilizar parámetros en nuestras consultas en lugar de concatenar directamente los valores.
¿Qué ocurre si algo falla?
Imaginemos que la primera actualización funciona correctamente:
UPDATE tipoproducto ...
pero la segunda operación falla:
UPDATE productos ...
Sin una transacción, podríamos terminar con la primera operación aplicada y la segunda sin aplicar.
Eso puede dejarnos la información en un estado inconsistente.
Con una transacción, en cambio, podemos hacer:
$query->rollBack();
y deshacer los cambios realizados dentro de esa transacción.
La idea puede resumirse de una forma muy sencilla:
Todo correcto → COMMIT
Algo falla → ROLLBACK
¿Cuándo tiene sentido utilizar transacciones?
No necesitamos utilizar una transacción para absolutamente cualquier consulta.
Son especialmente útiles cuando tenemos varias operaciones que dependen unas de otras y necesitamos garantizar que se ejecutan como una única unidad lógica.
Algunos ejemplos habituales:
- Crear un pedido y sus líneas.
- Actualizar el stock y registrar el movimiento correspondiente.
- Transferir dinero entre cuentas.
- Modificar información relacionada en varias tablas.
- Crear registros dependientes entre sí.
- Realizar procesos de migración o actualización de datos.
En todos estos casos puede ser importante evitar que una parte de la operación se complete mientras otra queda a medias.
En resumen
Las transacciones SQL nos permiten ejecutar varias operaciones como una unidad de trabajo.
En nuestro ejemplo:
- Iniciamos la transacción.
- Actualizamos la categoría.
- Actualizamos los productos relacionados.
- Si todo funciona, hacemos
COMMIT. - Si algo falla, hacemos
ROLLBACK.
Y con esto evitamos uno de esos problemas que aparecen cuando menos nos interesa: tener una parte de nuestros datos actualizada y otra parte a medias.
La idea importante
Una transacción no consiste simplemente en ejecutar varias consultas seguidas. Consiste en conseguir que esas operaciones formen parte de una misma unidad lógica: o se aplican todas, o deshacemos los cambios.
Y para un caso como el que planteábamos al principio, donde necesitamos modificar información relacionada en varias tablas, es precisamente lo que necesitamos.
Un saludo.
Visitas: 702
Automation & Data Specialist | Web Development | Data Processing & Integration

