En SQL, las transacciones se utilizan para mantener la integridad de los datos al garantizar que una secuencia de instrucciones SQL se ejecute por completo o no se ejecute en absoluto.
Las transacciones gestionan secuencias de sentencias SQL que deben ejecutarse como una sola unidad de trabajo, de modo que la base de datos nunca contenga los resultados de operaciones parciales.
Cuando una transacción realiza varios cambios en la base de datos, todos los cambios se realizan correctamente cuando se confirma la transacción o todos los cambios se deshacen cuando se revierte la transacción.
¿Cuándo usar una transacción?
Las transacciones son primordiales en situaciones en las que la integridad de los datos estaría en riesgo en caso de que fallara cualquiera de una secuencia de instrucciones SQL.
Por ejemplo, si estuviera moviendo dinero de una cuenta bancaria a otra, necesitaría deducir dinero de una cuenta y agregarlo a la otra. No querrá que falle a la mitad, de lo contrario, el dinero podría debitarse de una cuenta pero no acreditarse en la otra.
Las posibles razones de la falla podrían incluir fondos insuficientes, número de cuenta no válido, falla de hardware, etc.
Así que no quiero estar en una situación en la que permanezca así:
Debit account 1 (Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
Eso sería realmente malo. La base de datos tendría datos inconsistentes y el dinero desaparecería en el aire. Entonces el banco perdería un cliente (el banco probablemente perdería a todos sus clientes si esto siguiera sucediendo) y usted perdería su trabajo.
Para guardar su trabajo, podría usar una transacción que sería algo como esto:
START TRANSACTION
Debit account 1
Credit account 2
Record transaction in transaction journal
END TRANSACTION
Podría escribir lógica condicional dentro de esa transacción que revierta la transacción si algo sale mal.
Por ejemplo, si algo sale mal entre el débito de la cuenta 1 y el crédito de la cuenta 2, se revierte toda la transacción.
Por lo tanto, solo habría dos resultados posibles:
Debit account 1 (Not Done)
Credit account 2 (Not Done)
Record transaction in transaction journal (Not Done)
O:
Debit account 1 (Done)
Credit account 2 (Done)
Record transaction in transaction journal (Done)
Esta es una descripción simplificada, pero es una ilustración clásica de cómo funcionan las transacciones SQL. Las transacciones SQL tienen ACID.
Tipos de transacciones
Las transacciones SQL se pueden ejecutar en los siguientes modos.
Modo de transacción | Descripción |
---|---|
Transacción de confirmación automática | Cada declaración individual es una transacción. |
Transacción implícita | Una nueva transacción se inicia implícitamente cuando se completa la transacción anterior, pero cada transacción se completa explícitamente, normalmente con un COMMIT o ROLLBACK declaración dependiendo del DBMS. |
Transacción explícita | Comenzó explícitamente con una línea como START TRANSACTION , BEGIN TRANSACTION o similar, según el DBMS, y confirmado o revertido explícitamente con las declaraciones relevantes. |
Transacción por lotes | Aplicable solo a múltiples conjuntos de resultados activos (MARS). Una transacción explícita o implícita que se inicia en una sesión de MARS se convierte en una transacción por lotes. |
Los modos exactos y las opciones disponibles pueden depender del DBMS. Esta tabla describe los modos de transacción disponibles en SQL Server.
En este artículo, nos centramos principalmente en las transacciones explícitas.
Consulte Cómo funcionan las transacciones implícitas en SQL Server para ver una discusión sobre la diferencia entre las transacciones implícitas y la confirmación automática.
Sytnax
La siguiente tabla describe la sintaxis básica para iniciar y finalizar una transacción explícita en algunos de los DBMS más populares.
DBMS | Sintaxis de transacciones explícitas |
---|---|
MySQL, MariaDB, PostgreSQL | Las transacciones explícitas comienzan con START TRANSACTION o BEGIN declaración. COMMIT confirma la transacción actual, haciendo que sus cambios sean permanentes. ROLLBACK revierte la transacción actual, cancelando sus cambios. |
SQLite | Las transacciones explícitas comienzan con BEGIN TRANSACTION declaración y terminar con el COMMIT o ROLLBACK declaración. También puede terminar con END declaración. |
Servidor SQL | Las transacciones explícitas comienzan con BEGIN TRANSACTION declaración y terminar con el COMMIT o ROLLBACK declaración. |
Oráculo | Las transacciones explícitas comienzan con SET TRANSACTION declaración y terminar con el COMMIT o ROLLBACK declaración. |
En muchos casos, ciertas palabras clave son opcionales cuando se usan transacciones explícitas. Por ejemplo, en SQL Server y SQLite, simplemente podría usar BEGIN
(en lugar de BEGIN TRANSACTION
) y/o podría terminar con COMMIT TRANSACTION
(a diferencia de simplemente COMMIT
).
También hay varias otras palabras clave y opciones que puede especificar al crear una transacción, así que consulte la documentación de su DBMS para conocer la sintaxis completa.
Ejemplo de transacción SQL
Aquí hay un ejemplo de una transacción simple en SQL Server:
BEGIN TRANSACTION
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION;
En este caso, la información del pedido se elimina de dos tablas. Ambas declaraciones se tratan como una unidad de trabajo.
Podríamos escribir lógica condicional en nuestra transacción para hacerla retroceder en caso de error.
Nombrar una transacción
Algunos DBMS le permiten proporcionar un nombre para sus transacciones. En SQL Server, puede agregar el nombre elegido después de BEGIN
y COMMIT
declaraciones.
BEGIN TRANSACTION MyTransaction
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction;
Ejemplo 1 de reversión de transacciones SQL
Aquí está el ejemplo anterior de nuevo, pero con algo de código extra. El código adicional se utiliza para deshacer la transacción en caso de error.:
BEGIN TRANSACTION MyTransaction
BEGIN TRY
DELETE OrderItems WHERE OrderId = 5006;
DELETE Orders WHERE OrderId = 5006;
COMMIT TRANSACTION MyTransaction
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION MyTransaction
END CATCH
El TRY...CATCH
La declaración implementa el manejo de errores en SQL Server. Puede encerrar cualquier grupo de declaraciones T-SQL en un TRY
cuadra. Luego, si ocurre un error en el TRY
bloque, el control se pasa a otro grupo de sentencias que está encerrado en un CATCH
bloquear.
En este caso, usamos el CATCH
bloquear para deshacer la transacción. Dado que está en el CATCH
bloqueo, la reversión solo ocurre si hay un error.
Ejemplo 2 de reversión de transacciones SQL
Echemos un vistazo más de cerca a la base de datos de la que acabamos de eliminar filas.
En el ejemplo anterior, eliminamos filas de Orders
y OrderItems
tablas en la siguiente base de datos:

En esta base de datos, cada vez que un cliente realiza un pedido, se inserta una fila en Orders
tabla y una o más filas en OrderItems
mesa. El número de filas insertadas en OrderItems
depende de cuántos productos diferentes pida el cliente.
Además, si se trata de un nuevo cliente, se inserta una nueva fila en Customers
mesa.
En ese caso, las filas deben insertarse en tres tablas.
En caso de falla, no nos gustaría tener una fila insertada en Orders
tabla pero sin filas correspondientes en OrderItems
mesa. Eso daría como resultado un pedido sin ningún artículo de pedido. Básicamente, queremos que ambas tablas estén completamente actualizadas o nada en absoluto.
Fue lo mismo cuando eliminamos las filas. Queríamos eliminar todas las filas o ninguna.
En SQL Server, podríamos escribir la siguiente transacción para INSERT
declaraciones.
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
Este ejemplo asume que hay una lógica en otro lugar que determina si el cliente ya existe o no en la base de datos.
El cliente podría haber sido insertado fuera de esta transacción:
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
BEGIN TRANSACTION
BEGIN TRY
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
END CATCH
Si la transacción falla, el cliente aún estaría en la base de datos (pero sin ningún pedido). La aplicación necesitaría verificar si el cliente ya existe antes de realizar la transacción.
Transacción SQL con puntos de guardado
Un punto de guardado define una ubicación a la que una transacción puede regresar si parte de la transacción se cancela condicionalmente. En SQL Server, especificamos un punto de guardado con SAVE TRANSACTION savepoint_name
(donde nombre_guardarpunto es el nombre que le damos al savepoint).
Reescribamos el ejemplo anterior para incluir un punto de guardado:
BEGIN TRANSACTION
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
ROLLBACK TRANSACTION StartOrder;
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;
Aquí, hemos establecido un punto de guardado justo después del cliente INSERT
declaración. Más adelante en la transacción, uso el ROLLBACK
instrucción para indicar a la transacción que retroceda a ese punto de guardado.
Cuando ejecuto esa declaración, se inserta el cliente, pero no se inserta ninguna información del pedido.
Si una transacción se retrotrae a un punto de guardado, debe continuar hasta completarse con más sentencias SQL si es necesario y una COMMIT TRANSACTION
estado de cuenta, o debe cancelarse por completo revirtiendo toda la transacción.
Si muevo el ROLLBACK
declaración de vuelta al anterior INSERT
declaración, así:
BEGIN TRANSACTION
INSERT INTO Customers ( CustomerId, CustomerName, PostalAddress, City, StateProvince, ZipCode, Country, Phone )
VALUES (1006, 'Hi-Five Solutionists', '5 High Street', 'Highlands', 'HI', '1254', 'AUS', '(415) 413-5182');
SAVE TRANSACTION StartOrder;
INSERT INTO Orders ( OrderId, OrderDate, CustomerId )
VALUES ( 5006, SYSDATETIME(), 1006 );
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 1, 1, 20, 25.99 );
ROLLBACK TRANSACTION StartOrder;
INSERT INTO OrderItems ( OrderId, OrderItemId, ProductId, Quantity, ItemPrice )
VALUES ( 5006, 2, 7, 120, 9.99 );
COMMIT TRANSACTION;
SELECT @@TRANCOUNT;
Esto produce un error de conflicto de clave externa. Específicamente, recibo el siguiente error:
(1 row affected) (1 row affected) (1 row affected) Msg 547, Level 16, State 0, Line 13 The INSERT statement conflicted with the FOREIGN KEY constraint "FK_OrderItems_Orders". The conflict occurred in database "KrankyKranes", table "dbo.Orders", column 'OrderId'. The statement has been terminated. (1 row affected)
Esto ocurrió porque, aunque el pedido ya se había insertado, esa operación se deshizo cuando retrocedimos al punto de guardado. Entonces la transacción procedió a su finalización. Pero cuando encontró el artículo del pedido final, no había ningún pedido correspondiente (porque se había deshecho) y obtuvimos el error.
Cuando verifiqué la base de datos, se insertó el cliente, pero nuevamente, no se insertó ninguna información del pedido.
Puede hacer referencia al mismo punto de guardado desde varios lugares en la transacción si es necesario.
En la práctica, usaría la programación condicional para devolver la transacción a un punto de guardado.
Transacciones anidadas
También puede anidar transacciones dentro de otras transacciones si es necesario.
Así:
BEGIN TRANSACTION Transaction1;
UPDATE table1 ...;
BEGIN TRANSACTION Transaction2;
UPDATE table2 ...;
SELECT * from table1;
COMMIT TRANSACTION Transaction2;
UPDATE table3 ...;
COMMIT TRANSACTION Transaction1;
Como se mencionó, la sintaxis exacta que utilice para crear una transacción dependerá de su DBMS, así que consulte la documentación de su DBMS para obtener una imagen completa de sus opciones al crear transacciones en SQL.