1.3 事务控制语句
事务是对数据库操作的逻辑单位,在一个事务中可以包含一条或多条DML(数据操纵语言)、DDL(数据定义语言)和DCL(数据控制语言)语句,这些语句组成一个逻辑整体。事务的执行只有两种结果:要么全部执行,把数据库带入一个新的状态;要么全部不执行,对数据库不做任何修改。对事务的操作有两个:提交(COMMIT)和回滚(ROLLBACK)。提交事务时,对数据库所做的修改便永久写入数据库。回滚事务时,对数据库所做的修改全部撤销,数据库恢复到操作前的状态。事务可用于操作数据库的任何场合,包括应用程序、存储过程、触发器等。
我们拿现实生活中的一个例子来说明事务的概念。在两个银行账号之间转账时,首先从第一个账号中减去转账金额,然后在第二个账号中加上相等数量的金额,这两个操作必须作为一个整体来完成,不允许只进行第一个操作而不进行第二个操作,也不允许只进行第二个操作而不进行第一个操作。如果这两个操作都顺利完成,那么这个事务可以提交,这次转账成功。否则如果有一个操作失败,那么这个事务必须回滚,这次转账失败,两个账号的状态都恢复到操作以前的状态。在数据库系统中,类似这样可以看做一个整体的多个操作就是一个事务。
事务具有四个属性,这四个属性的英文单词首字母合在一起就是ACID。这四个属性是:
·原子性(Atomicity):事务要么全部执行,要么全部不执行,不允许部分执行。
·一致性(Consistency):事务把数据库从一个一致状态带入另一个一致状态。
·独立性(Isolation):一个事务的执行不受其他事务的影响。
·持续性(Durability):一旦事务提交,就永久有效,不受关机等情况的影响。
一个事务中可以包含多条DML语句,或者包含一条DDL语句,或者包含一条DCL语句。事务开始于第一条SQL语句,在下列之一情况下结束:
·遇到COMMIT或ROLLBACK命令。
·遇到一条DDL或者DCL命令。
·系统发生错误、退出或者崩溃。
总之,事务是一系列可以把系统带入一个新的状态的操作,如果事务被提交,则数据库进入一个新的状态,否则数据库恢复到事务以前的状态。在数据库中使用事务的好处是首先可以确保数据的一致性,其次在对数据做永久修改之前可以预览以前的数据改变,还可以将逻辑上相关的操作进行分组。
控制事务的方式有两种,一种是隐式控制,数据库管理系统根据实际情况决定提交事务还是回滚事务;另一种方式是显式控制,在事务的最后放置一条COMMIT或ROLLBACK命令,将事务提交或回滚。
如果是隐式控制,那么事务在遇到一条DDL命令,如CREATE,或者遇到一条DCL命令,如GRANT,或者从SQLPlus正常退出,即使没有发出COMMIT或ROLLBACK命令,这个事务将被自动提交。如果从SQLPlus非正常退出或发生系统崩溃,那么系统将自动回滚事务。
如果是显式控制,那么在事务的最后就要通过COMMIT命令提交事务,或者通过一条ROLLBACK命令回滚事务。
如果事务被提交,那么对数据库所作的修改将写入数据库。如果回滚事务,一般情况下将回滚到事务的开始,即对数据库不做任何修改。在Oracle中,允许部分回滚事务,即可以将事务有选择地回滚到中间的某个点。部分回滚是通过设置保存点(SAVEPOINT)来实现的。在事务中可以通过SAVEPOINT命令设置若干个保存点,这样可以将事务有选择地回滚到某一个保存点。图1.1表示对事务的提交、回滚和保存点操作。
图 1.1 事务控制
在图1.1所示的事务中,有一条INSERT语句,一条UPDATE语句和一条DELETE语句,并设置了两个保存点。如果在事务的最后执行了COMMIT命令,则这三条语句都将对数据库产生影响。如果在事务的最后执行了ROLLBACK命令,则事务回退到事务的开始,这三条语句都数据库不产生任何影响。如果执行了命令ROLLBACK TO B,则事务回退到保存点B,这时仅有INSERT和UPDATE语句对数据库产生影响,DELETE语句的执行结果被撤销。如果继续执行命令ROLLBACK TO A,则事务回退到保存点A,这时UPDATE和DELETE语句的执行结果被撤销。如果继续执行COMMIT命令,则只有INSERT语句的执行结果被写入数据库。
用户访问数据库时,数据库中的数据是放在缓冲区中的,当前用户可以通过查询操作,浏览对数据操作的结果。如果没有提交事务,其他用户是看不到事务的修改结果的。当一个用户修改表中的数据时,将对被修改的数据加锁,其他用户无法在此期间对该行数据进行修改,直到这个用户提交或回滚这个事务。
如果在事务的最后执行了COMMIT命令,则对数据的修改将被写入数据库,以前的数据将永久丢失,无法恢复,其他用户都可以浏览修改后的结果,在数据上加的锁被释放,其他用户可以对数据执行新的修改,在事务中设置的所有保存点将被删除。
下面的语句序列演示了在SQL*Plus中执行一条DML语句,然后执行COMMIT命令的情况,并显示了执行的结果。
SQL>INSERT INTO dept VALUES(60,'HHHHH','HHHHHH');
已创建1行。
SQL>COMMIT;
提交完成。
如果在事务的最后执行了ROLLBACK命令,那么所有未提交的修改将被丢弃,对数据所做的修改将被取消,数据恢复到修改以前的状态,在行上加的锁被释放,其他用户可以对这样的数据进行新的修改。
下面的语句序列演示了在SQL*Plus中执行一条DML语句,然后执行ROLLBACK命令的情况,并显示了执行的结果。
SQL>DELETE FROM dept WHERE deptno=60;
已删除1行。
SQL>ROLLBACK;
回退已完成。
如果在事务中设置了保存点,并且在事务的最后执行ROLLBACK命令回滚到某个保存点,那么在此保存点之后的DML语句所做的修改将被丢弃,但是在此保存点之前的DML语句所做的修改仍然没有写入数据库,还可以进行提交或回滚。
下面的语句序列是在SQL*Plus中执行的两条DML语句,以及在两条DML语句之间设置的保存点,然后是回滚到这个保存点,最后提交这个事务的情况。
SQL>DELETE FROM dept WHERE deptno=60;
已删除1行。
SQL>SAVEPOINT a;
保存点已创建。
SQL>DELETE FROM dept WHERE deptno=40;
已删除1行。
SQL>ROLLBACK TO a;
回退已完成。
SQL>COMMIT;
提交完成。