我有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调用回滚到该点?
答案 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可能正在做的事情和其他连接器。