posts - 56, comments - 54, trackbacks - 0, articles - 4
   ::  ::  :: 联系 :: 聚合  :: 管理

PL/SQL支持嵌套事务

Posted on 2006-01-27 16:20 Terry的Blog 阅读(2235) 评论(1)  编辑  收藏 所属分类: oracle

使用场景:
当一个PL/SQL程序执行出错 需要输出log信息到数据库表中的时候 就会遇到这个难题.
log的内容要提交到表中 但是前面出错的内容却不能提交.虽然我们可以在出错时先Rollback 等log好了
再commit.但是这样毕竟比较麻烦.实际上PL/SQL本身支持嵌套事务.
看下面的小例子:
CREATE OR REPLACE procedure Test1
as
PRAGMA AUTONOMOUS_TRANSACTION; -- 关键就是这句
begin
--set transaction read write;
insert into aaa values(1,4,7);
commit;
end;
/

CREATE OR REPLACE procedure Test2
as
begin
--set transaction read write;
insert into aaa values(2,5,8);
Test1;
insert into aaa values(3,6,9);
rollback;
end;
/
当execute Test2完了时在AAA表中插入了一行(1,4,7)。

--------------------------------------------------------------------------------------------------------------------------
理论知识:
AUTONOMOUS TRANSACTION(自治事务)的介绍:

在基于低版本的ORACLE做一些项目的过程中,有时会遇到一些头疼的问题.,比如想在执行当前一个由多个DML组成的transaction(事务)时,为每一步DML记录一些信息到跟踪表中,由于事务的原子性,这些跟踪信息的提交将决定于主事务的commit或rollback. 这样一来写程序的难度就增大了, 程序员不得不把这些跟踪信息记录到类似数组的结构中,然后在主事务结束后把它们存入跟踪表.哎,真是麻烦!

有没有一个简单的方法解决类似问题呢?

ORACLE8i的AUTONOMOUS TRANSACTION(自治事务,以下AT)是一个很好的回答。

AT 是由主事务(以下MT)调用但是独立于它的事务。在AT被调用执行时,MT被挂起,在AT内部,一系列的DML可以被执行并且commit或rollback.

注意由于AT的独立性,它的commit和rollback并不影响MT的执行效果。在AT执行结束后,主事务获得控制权,又可以继续执行了。

如何实现AT的定义呢?我们来看一下它的语法。其实非常简单。

只需下列PL/SQL的声明部分加上PRAGMA AUTONOMOUS_TRANSACTION 就可以了。

1.  顶级的匿名PL/SQL块

2.  Functions 或 Procedure(独立声明或声明在package中都可)

3.  SQL Object Type的方法

4.  触发器。

  比如:

 在一个独立的procedure中声明AT

CREATE OR REPLACE PROCEDURE

   Log_error(error_msg IN VARCHAR2(100))

IS

   PRAGMA AUTONOMOUS_TRANSACTION;

BEGIN

   Insert into Error_log values ( sysdate,error_msg);

   COMMIT;

END;

下面我们来看一个例子,(win2000 advanced server + oracle8.1.6 , connect as scott)

建立一个表:

create table msg (msg varchar2(120));

首先,用普通的事务写个匿名PL/SQL块:

 declare

   cnt  number := -1;   --} Global variables

   procedure local is

   begin

      select count(*) into cnt from msg;

      dbms_output.put_line('local: # of rows is '||cnt);

      insert into msg values ('New Record');

      commit;

   end;

 

   begin

      delete from msg ;

      commit;

      insert into msg values ('Row 1');

      local;

      select count(*) into cnt from msg;

      dbms_output.put_line('main: # of rows is '||cnt);

      rollback;

      local;

      insert into msg values ('Row 2');

      commit;

      local;

      select count(*) into cnt from msg;

      dbms_output.put_line('main: # of rows is '||cnt);

   end;

运行结果(注意打开serveroutput)

local: # of rows is 1   -> 子程序local中可以’看到’主匿名块中的uncommitted记录

main: # of rows is 2    -> 主匿名块可以’看到’2条记录(它们都是被local commit掉的)

local: # of rows is 2   -> 子程序local首先’看到’2条记录,然后又commit了第三条记录

local: # of rows is 4   -> 子程序local又’看到’了新增加的记录(它们都是被local commit掉的),然后又commit了第五条记录

main: # of rows is 5    -> 主匿名块最后’看到’了所有的记录.

从这个例子中,我们看到COMMIT和ROLLBACK的位置无论是在主匿名块中或者在子程序中,都会影响到整个当前事务.

现在用AT改写一下匿名块中的procedure local:

...

   procedure local is

      pragma AUTONOMOUS_TRANSACTION;

   begin

...

重新运行(注意打开serveroutput)

local: # of rows is 0   -> 子程序local中无法可以’看到’主匿名块中的uncommitted记录 (因为它是独立的)

main: # of rows is 2    -> 主匿名块可以’看到’2条记录,但只有一条是被commited.

local: # of rows is 1   -> 子程序local中可以’看到’它前一次commit的记录,但是主匿名块中的记录已经被提前rollback了

local: # of rows is 3   -> 子程序local 中可以’看到’3条记录包括主匿名块commit的记录

main: # of rows is 4    ->主匿名块最后’看到’了所有的记录.

很明显,AT是独立的,在它执行时,MT被暂停了. AT的COMMIT,ROLLBACK并不影响MT的执行.

运用AT时,有一些注意事项,简单列举如下:

1.     在匿名PL/SQL块中,只有顶级的匿名PL/SQL块可以被设为AT

2.     如果AT试图访问被MT控制的资源,可能有deadlock发生.

3.     Package 不能被声明为AT,只有package所拥有的function和procedure 才能声明为AT

4.     AT程序必须以commit 或rollback结尾,否则会产生Oracle错误ORA-06519: active autonomous transaction detected and rolled back

在程序开发时,如果充分运用AUTONOMOUS TRANSACTION的特性,一定能取得事倍功半的效果.


评论

# re: PL/SQL支持嵌套事务  回复  更多评论   

2013-03-20 00:59 by
但是

只有注册用户登录后才能发表评论。


网站导航: