可能有人对事务(transaction)这个词并不熟悉,它通常被用于商务贸易或者经济活动中,但是在RDBMS中,事务是对表中数据进行更新的单位。简单来说,事务就是需要在同一个处理单元中执行的一系列更新处理的集合。
如前所述,对表进行更新需要使用INSERT, DELETE和UPDATE三种语句。但通常情况下,更新处理并不是执行一次就结束了,而是需要执行一系列连续的操作。这时,事务就能体现它的价值了。
说到事务的例子,请大家思考一下下述情况。
现在,请大家把自己想象为管理 Product(商品)表的程序员或者 软件工程师。销售部门的领导对你提出了如下要求。
“某某,经会议讨论,我们决定把运动 T 恤的销售单价下调 1000 日元, 同时把 T 恤衫的销售单价上浮 1000 日元,麻烦你去更新一下数据库。”
由于大家已经学习了更新数据的方法 —— 只需要使用 UPDATE 进行 更新就可以了,所以肯定会直接回答“知道了,请您放心吧”。
此时的事务由如下两条更新处理所组成。
更新商品信息的事务
1 将运动T恤的销售单价降低1000日元
UPDATE Product
SET sale_price = sale_price - 1000
WHERE product_name = '运动T恤';
2 将T恤衫的销售单价上浮1000日元
UPDATE Product
SET sale_price = sale_price + 1000
WHERE product_name = 'T恤衫';
上述1和2的操作一定要作为同一个处理单元执行。如果只执行了1 的操作而忘记了执行2的操作,或者反过来只执行了2的操作而忘记了执 行1的操作,一定会受到领导的严厉批评。遇到这种需要在同一个处理单 元中执行一系列更新操作的情况,一定要使用事务来进行处理。
创建事务
事务开始语句 ;
DML 语句1 ;
DML 语句2 ;
DML 语句3 ;
...
事务结束语句(COMMIT 或者 ROLLBACK);
在MYSQL中,上面的情境要用如下SQL解决:
START TRANSACTION;
-- 将运动T恤的销售单价降低1000日元 UPDATE Product
SET sale_price = sale_price - 1000
WHERE
product_name = '运动T恤';
-- 将T恤衫的销售单价上浮1000日元 UPDATE Product
SET sale_price = sale_price + 1000
WHERE
product_name = 'T恤衫';
COMMIT;
COMMIT
COMMIT是提交事务包含的全部更新处理的结束指令。相当于文件处理中的覆盖保存。一旦提交,就无法恢复到事务开始前的状态了。
事务回滚
BEGIN
TRANSACTION;
------------------- 1 -- 将运动T恤的销售单价降低1000日元 UPDATE Product
SET sale_price = sale_price - 1000
WHERE
product_name = '运动T恤';
-- 将T恤衫的销售单价上浮1000日元 UPDATE Product
SET sale_price = sale_price + 1000
WHERE
product_name = 'T恤衫';
ROLLBACK;
事务处理何时开始
之前我们说过,事务并没有标准的开始指令存在,而是根据 DBMS 的不同而不同。
实际上,几乎所有的数据库产品的事务都无需开始指令。这是因为大部分情况下,事务在数据库连接建立时就已经悄悄开始了,并不需要用户再明确发出开始指令。例如,使用 Oracle 时,数据库连接建立之后,第一条 SQL 语句执行的同时, 事务就已经悄悄开始了。
像这样不使用指令而悄悄开始事务的情况下,应该如何区分各个事务呢?通常
会有如下两种情况。
- 每条SQL语句就是一个事务(自动提交模式)
- 直到用户执行COMMIT或者ROLLBACK为止算作一个事务
通常的 DBMS 都可以选择其中任意一种模式。默认使用自动提交模式的 DBMS 有 SQL Server、PostgreSQL 和 MySQL 等 。该模式下的 DML 语句如下 所示,每一条语句都括在事务的开始语句和结束语句之中。
BEGIN TRANSACTION;
-- 将运动T恤的销售单价降低1000日元 UPDATE Product
SET sale_price = sale_price - 1000 WHERE product_name = '运动T恤';
COMMIT;
BEGIN TRANSACTION;
-- 将T恤衫的销售单价上浮1000日元 UPDATE Product
SET sale_price = sale_price + 1000 WHERE product_name = 'T恤衫';
COMMIT;
在默认使用第二种模式的 Oracle 中,事务都是直到用户自己执行提交或者回滚指令才会结束。
自动提交的情况需要特别注意的是 DELETE 语句。如果不是自动提交,即使使用 DELETE 语句删除了数据表,也可以通过 ROLLBACK 命令取消该事务的处 理,恢复表中的数据。但这仅限于明示开始事务,或者关闭自动提交的情况。如果 不小心在自动提交模式下执行了 DELETE 操作,即使再回滚也无济于事了。
这是一个很严重的问题,初学者难免会碰到这样的麻烦。一旦误删了数据,如果无法重 新插入,是不是想哭的心都有了?所以一定要特别小心。
网友评论