本章介绍什么是事务处理以及如何利用 COMMIT 和 ROLLBACK 语句来管理事务处理
20.1 事务处理
事务处理可以用来维护数据库的完整性,它保证成批的 SQL 操作要么完全执行,要么完全不执行
正如第 12 章所述,关系数据库设计把数据存储在多个表中,使数据更容易操纵、维护和重用。不用深究如何以及为什么进行关系数据库设计,在某种程度上说,设计良好的数据库模式都是关联的。
前面章中使用的 Orders 表就是一个很好的例子。
订单存储在 Orders 和 OrderItems 两个表中:Orders 存储实际的订单,而 OrderItems 存储订购的各项物品。这两个表使用主键互相关联。这两个表又与包含客户和产品信息的其他表相关联。
给系统添加订单的过程如下:
1.检查数据库中是否存在相应的客户,如果不存在,添加他/她
2.检索客户的 ID
3.添加一行到 Orders 表,把它与客户 ID 关联
4.检索 Orders 表中赋予的新订单 ID
5.对于订购的每个物品在 OrderItems 表中添加一行,通过检索出来的 ID 把它与 Orders 表关联(以及通过产品 ID 与 Products 表关联)
假如由于某种数据库故障(如超出磁盘空间、安全限制、表锁等)阻止了这个过程的完成。那么数据库中的数据会出现什么情况?
- 如果故障发生在添加了客户之后,Orders 表添加之前,那么不会有什么问题
- 因为某些客户没有订单是完全合法的
- 如果故障发生在 Orders 行插入之后,OrderItems 添加之前,数据库中则会有一个空订单
- 如果系统在添加 OrderItems 行之中出现故障,数据库中存在不完整的订单,而且还不知道
这就需要使用事务处理:事务处理是一种机制,用来管理必须成批执行的 SQL 操作,以保证数据库不包含不完整的操作结果
利用事务处理,可以保证一组操作不会中途停止,它们或者作为整体执行,或者完全不执行
如果没有错误发生,整组语句提交给(写到)数据库表;
如果发生错误,则进行回退(撤销)以恢复数据库到某个已知且安全的状态
因此,请看相同的例子,这次我们说明过程如何工作:
1.检查数据库中是否存在相应的客户,如果不存在,添加他/她
2.提交客户信息
3.检索客户的 ID
4.添加一行到 Orders 表
5.如果在添加行到 Orders 表时出现故障,则回退
6.检索 Orders 表中赋予的新订单 ID
7.对于订购的每项物品,添加新行到 OrderItems 表
8.如果在添加新行到 OrderItems 时出现故障,回退所有添加的 OrderItems 行和 Orders 行
有关事务处理需要知道的几个术语:
- 事务指一组 SQL 语句
- 回退指撤销指定 SQL 语句的过程
- 提交指将未存储的 SQL 语句结果写入数据库表
- 保留点指事务处理中设置的临时占位符,你可以对它发布回退(与回退整个事务处理不同)
可以回退哪些语句?
事务处理用来管理 INSERT、UPDATE 和 DELETE 语句
不能回退 SELECT 语句,也不能回退 CREATE 或 DROP 操作
20.2 控制事务处理
下面讨论事务处理的管理中所涉及的问题
管理事务处理的关键在于将 SQL 语句组分解为逻辑块,并明确规定数据何时应该回退,何时不应该回退
有的 DBMS 要求明确标识事务处理块的开始和结束,如在 SQL Server 中,标识如下:
BEGIN TRANSACTION
...
COMMIT TRANSACTION
MySQL 中等同的代码为:
START TRANSATCION
...
在 Oracle 数据库中,没有提供开始事务处理语句,所有的事务都是隐式开始的,也就是说在 Oracle 中,用户不可以显示使用命令来开始一个事务
Oracle 任务第一条修改数据库的语句,或者一些要求事务处理的场合都是事务的隐式开始
20.2.1 使用 ROLLBACK
SQL 的 ROLLBACK 命令用来回退(撤销)SQL 语句,请看下面的语句:
DELETE FROM Orders;
ROLLBACK;
20.2.2 使用 COMMIT
一般的 SQL 语句都是直接针对数据库表执行和编写的,这就是所谓的隐含提交,也就是提交(写或保存)操作都是自动进行的
但是,在事务处理块中,提交不会隐含地进行,为进行明确的提交,需要使用 COMMIT 语句。
看一个 SQL Server 的例子:
BEGIN TRANSACTION
DELETE OrderItems WHERE order_num = 12345
DELETE Orders WHERE order_num = 12345
COMMIT TRANSACTION
为在 Oracle 中完成相同的工作,可如下进行:
DELETE OrderItems WHERE order_num = 12345;
DELETE Orders WHERE order_num = 12345;
COMMIT;
20.2.3 使用保留点
简单的 ROLLBACK 和 COMMIT 语句就可以写入或撤销整个事务处理。
但是,这只是针对简单的事务处理才能这样做,更复杂的事务处理可能需要部分提交或回退
例如,前面描述的添加订单的过程为事务处理。如果发生错误,只需要返回到添加 Orders 行之前即可。不需要回退到 Customers 表(如果存在的话)。
为了完成这样的事情,必须在事务处理中合适的位置放置占位符。这样,如果需要回退,就可以回退到某个占位符,
在 SQL 中,这些占位符称为保留点
为了在 MySQL 和 Oracle 中创建占位符,可如下使用 SAVEPOINT 语句:
SAVEPOINT delete1;
每个保留点都取标识它的唯一名字,以便在回退时,DBMS 知道要回退到何处。
为了回退到上面给出的保留点,在 MySQL 和 Oracle 中,可如下进行:
ROLLBACK TO delete1;
保留点越多越好:保留点越多,就越能按自己的意愿灵活地进行回退