何时在PHP执行Oracle PL / SQL-OCI8时会执行自动回滚?

时间:2010-06-22 15:59:10

标签: php oracle plsql transactions

我有PHP代码执行存储过程10次。如果一个存储过程调用失败,它应该继续,并在最后提交事务。

它基本上是这样的:

$connection = getConn();

foreach($row as $i=>$j) {
  $SQL = "BEGIN MYPROC.EXECUTE(:VAL1, :VAL2); END;";
  $statement = OCIParse($connection, $SQL);

  oci_bind_by_name($statement, 'VAL1', $row[i]['FIRSTVAL']);
  oci_bind_by_name($statement, 'VAL2', $row[i]['SECONDVAL']);

  $success = @OCIExecute($statement, OCI_DEFAULT);
  if(!$success) {
    print 'Exception in stored proc call';
  }
  else {
    print 'Success';
  }

}
oci_commit($connection);

我的问题是,如果在第5个存储过程调用中引发异常,是否会将所有存储的proc调用回滚到该点?

3 个答案:

答案 0 :(得分:1)

只要每个过程在同一个会话中执行,并且它们都没有发出提交,那么它们所做的更改就可以回滚。您应该在循环外打开连接,然后在其中完成所有工作。就像现在一样,你每次都要通过循环进行连接,效率低下,不允许你想做什么。您还应该在循环外部使用commit语句。

这样的事情,也许是:

$SQL = "BEGIN MYPROC.EXECUTE(:VAL1, :VAL2); END;";
$connection = getConn();
$statement = OCIParse($connection, $SQL);

foreach($row as $i=>$j) {

  oci_bind_by_name($statement, 'VAL1', $row[i]['FIRSTVAL']);
  oci_bind_by_name($statement, 'VAL2', $row[i]['SECONDVAL']);

  $success = @OCIExecute($statement, OCI_DEFAULT);
  if(!$success) {
    print 'Exception in stored proc call';
    oci_rollback($connection);
    exit processing here... 
  }
  else {
    print 'Success';
  }
}
oci_commit($connection);

答案 1 :(得分:1)

我认为PHP驱动程序而不是Oracle正在控制提交。 This似乎表明,从PHP 5.3.2(PECL OCI8 1.4)开始,每次调用OCIExecute(默认情况下)都会提交语句,而不管存储过程中是什么。

答案 2 :(得分:1)

最近不得不对此进行一些测试。当发生未处理的异常时,似乎Oracle会进行部分回滚,直到最顶层包含开始块或提交同一会话(并不总是一直回到先前的提交)。给定一个具有int id和varchar2 val以及proc:

的表
CREATE OR REPLACE PROCEDURE PROC_AUTO_COMMIT_TEST( 
   p_id int, p_val varchar2, p_cmd varchar2
) IS
BEGIN
   if (p_cmd = 'init') then
        delete from TEMP_AUTOCOMMIT_TEST;
        insert into TEMP_AUTOCOMMIT_TEST values(1,'one');
        insert into TEMP_AUTOCOMMIT_TEST values(2,'two');
        insert into TEMP_AUTOCOMMIT_TEST values(3,'three');
        commit;  
   else   
        update TEMP_AUTOCOMMIT_TEST 
           set val = p_val
         where id = p_id;

         if (p_cmd = 'throw') then
            insert into TEMP_AUTOCOMMIT_TEST values(3,'THREE');  -- throws
         end if;
   end if;     
END PROC_AUTO_COMMIT_TEST;

然后执行:

begin
    PROC_AUTO_COMMIT_TEST(0, null, 'init');   
    begin
        PROC_AUTO_COMMIT_TEST(1, 'ONE', null);
    end;
    begin
        PROC_AUTO_COMMIT_TEST(2, 'TWO', null);
        PROC_AUTO_COMMIT_TEST(3, 'THREE', 'throw');
    end;
end;

一直回滚到'init'中的提交(也回滚一次)。

按顺序排除这些(从任何一个Toad(自动提交关闭,每个块上的F9,整个事件的f5)或Sqlplus与/之间):

begin
    PROC_AUTO_COMMIT_TEST(0, null, 'init');   
end;

begin
    PROC_AUTO_COMMIT_TEST(1, 'ONE', null);
end;

begin
    PROC_AUTO_COMMIT_TEST(2, 'TWO', null);
    PROC_AUTO_COMMIT_TEST(3, 'THREE', 'throw');
end;

在THREE中出现的异常然后回到'ONE'之后。然而,'ONE'仍然需要回滚或提交,因为它持有行锁(在TOAD中使用会话浏览器验证)。将此调用为部分回滚,因为它不会一直返回到'init'调用中的提交并使行保持锁定状态。我假设这种情况更接近PHP可能正在做的事情和其他连接器。