以下文字摘自oracle文档 Oracle®数据库PL / SQL语言参考11g第1版(11.1) :
注意粗体文字,这是真的吗?我很好奇,所以我写了下面的例子来测试它。未处理的异常也会影响 子程序。如果退出子程序 成功,PL / SQL为其分配值 OUT参数。但是,如果你退出 带有未处理的异常,PL / SQL 不为OUT分配值 参数(除非它们是NOCOPY 参数)。 另外,如果存储了 子程序失败,未处理 异常,PL / SQL不回滚 子程序完成的数据库工作。
-- create a test table
CREATE TABLE e AS SELECT * FROM HR.EMPLOYEES;
-- create p1 which will update a row in table e
CREATE OR REPLACE PROCEDURE p1
IS
ex EXCEPTION;
row e%ROWTYPE;
BEGIN
select * into row from e where employee_id=100;
row.employee_id := 100;
row.first_name := 'yang';
-- update
UPDATE e set ROW = row where employee_id = 100;
-- and raise an error
RAISE ex;
END;
BEGIN
-- call the upper procedure
p1;
END;
-- test whether update success
select * from e where employee_id=100;
-- the upper query gives me
Steven
所以我的问题是:我是对的吗?
答案 0 :(得分:9)
在SO上查看此问题:Does Oracle roll back the transaction on an error?
在您的情况下,过程P1将成功或失败并回滚其更改。为什么它看起来像文档中的陈述相反(p1在程序中间失败并且未完成的工作)?
答案在于sentence just before your quote:
请记住,如果找不到引发异常的处理程序,PL / SQL会向主机环境返回未处理的异常错误,从而确定结果。例如,在Oracle预编译器环境中,将回滚由失败的SQL语句或PL / SQL块所做的任何数据库更改。
这意味着当程序失败时,如果未处理引发的异常,则将回滚不完整的工作。但是,如果捕获了异常而没有重新引发异常,则不完整的工作将保持不变。
我们可以通过在您的示例中添加一个WHEN OTHERS块(而不是重新引发异常 - 当然这是非常糟糕的主意,请参阅下面的原因)来显示此行为:
SQL> BEGIN
2 -- call the upper procedure
3 p1;
4 EXCEPTION
5 WHEN OTHERS THEN
6 dbms_output.put_line('log error...');
7 END;
8 /
log error...
PL/SQL procedure successfully completed
SQL> select employee_id, first_name from e where employee_id = 100;
EMPLOYEE_ID FIRST_NAME
----------- --------------------
100 yang
你真的 never want to do this :我们留下了未完成的工作,记录了错误,并且没有重新提出它我们有一个潜在的严重错误。此外,默默地忽略异常是灾难的一种方法。
答案 1 :(得分:2)
“此外,如果存储的子程序因未处理的异常而失败,PL / SQL不会回滚子程序完成的数据库工作。”
以上引用具体是指存储的子程序,但下面的代码块是匿名块,而不是存储的子程序
BEGIN
-- call the upper procedure
p1;
END;
因此,报价不适用。顶级匿名块的失败是执行回滚的失败(就像任何其他SQL语句一样) 使用以下代码进行测试表明,在触发SERVERERROR触发器时(即返回主机之前),值1和10的INSERT已经回滚(因为1的重新插入不会失败)重复密钥或死锁)。
drop table test_se_auto_tbl;
create table test_se_auto_tbl (id number(2) primary key, val varchar2(20));
create or replace trigger test_se_auto_trg after servererror on schema
begin
for c_rec in (select id, val from test_se_auto_tbl) loop
dbms_output.put_line(c_rec.id||':'||c_rec.val);
end loop;
dbms_output.put_line('Trigger');
insert into test_se_auto_tbl values (1,'test ');
end;
/
begin
insert into test_se_auto_tbl values (1,'test ');
insert into test_se_auto_tbl values (10,'test 10');
insert into test_se_auto_tbl values (100,'test 100');
end;
/
select id, val from test_se_auto_tbl;
支持这一假设的另一种情况。在这种情况下,匿名PL / SQL块在嵌套在另一个PL / SQL块内的EXECUTE IMMEDIATE中调用。虽然外部块捕获了异常,但是当EXECUTE IMMEDIATE运行原子语句时,插入已经回滚。
DECLARE
v_num NUMBER;
begin
begin
execute immediate
'declare
v_num number(2);
begin
insert into dummy values (1);
dbms_output.put_line(101);
v_num := 100;
end;';
exception
when others then null;
end;
select count(*) into v_num from dummy;
dbms_output.put_line(v_num);
end;
/