我想利用Oracle批量DML操作和异常处理。我的要求是在数据库视图上执行DML操作,它通过视图触发器执行一些验证,然后最终插入/更新基础表。但是,Oracle的FORALL .. SAVE EXCEPTIONS似乎没有捕获视图中引发的验证错误。这是SAVE EXCEPTION的限制/限制,它只适用于数据库表而不是视图吗? Oracle文档似乎也没有提到这一点。以下是我的测试代码(基于Handling Exceptions in Bulk Operations的修改):
创建表格:
create table exception_test (
id number(10) not null
);
在表格上创建视图:
create or replace view exception_test_v as
select exception_test.id id
,sysdate daytime
from exception_test;
在视图上创建触发器:
create or replace trigger iud_exception_test
instead of insert or update or delete on exception_test_v
for each row
declare
begin
if inserting then
if nvl(:new.id, 0) = 0 then
RAISE_APPLICATION_ERROR(-20815, 'ID must not be null!');
end if;
insert into exception_test (id) values (:new.id);
end if;
end;
/
在数据库视图上测试DML代码:
declare
TYPE t_tab IS TABLE OF exception_test_v%ROWTYPE;
l_tab t_tab := t_tab();
l_error_count NUMBER;
ex_dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);
BEGIN
-- Fill the collection.
FOR i IN 1 .. 100 LOOP
l_tab.extend;
l_tab(l_tab.last).id := i;
END LOOP;
-- Cause a failure.
l_tab(50).id := NULL;
l_tab(51).id := NULL;
EXECUTE IMMEDIATE 'TRUNCATE TABLE exception_test';
-- Perform a bulk operation.
BEGIN
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO exception_test_v (id)
VALUES (l_tab(i).id);
EXCEPTION
WHEN ex_dml_errors THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
END;
测试代码在视图触发器的索引50上输出错误,而不是处理完整的100次插入并捕获索引50和51上的错误以进行后检查。
对此的任何反馈都将非常感谢!
答案 0 :(得分:1)
立即忘记INSTEAD OF
触发器。让我们关注BULK EXCEPTIONS
部分。我的测试用例有一个ID
列为NOT NULL
列的表格。一个视图。我将使用FORALL INSERT
并尝试通过集合中的索引50和51将NULL
值插入VIEW
。期望在尝试在EXCEPTION
中插入NULL
时获得VIEW
:
SQL> create table exception_test (
2 ID NUMBER(10) NOT NULL
3 );
Table created.
SQL>
SQL>
SQL> create or replace view exception_test_v as
2 select exception_test.id id
3 ,SYSDATE DAYTIME
4 from exception_test;
View created.
SQL>
SQL> declare
2 TYPE t_tab IS TABLE OF exception_test_v%ROWTYPE;
3
4 l_tab t_tab := t_tab();
5 l_error_count NUMBER;
6
7 ex_dml_errors EXCEPTION;
8 PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);
9 BEGIN
10 -- Fill the collection.
11 FOR i IN 1 .. 100 LOOP
12 l_tab.extend;
13 l_tab(l_tab.last).id := i;
14 END LOOP;
15
16 -- Cause a failure.
17 l_tab(50).id := NULL;
18 l_tab(51).id := NULL;
19
20 EXECUTE IMMEDIATE 'TRUNCATE TABLE exception_test';
21
22 -- Perform a bulk operation.
23 BEGIN
24 FORALL I IN 1 .. L_TAB.COUNT SAVE EXCEPTIONS
25 INSERT INTO exception_test_v (id)
26 VALUES (L_TAB(I).ID);
27 EXCEPTION
28 WHEN EX_DML_ERRORS THEN
29 dbms_output.put_line('Inside exception');
30 l_error_count := SQL%BULK_EXCEPTIONS.count;
31 DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
32 FOR I IN 1 .. L_ERROR_COUNT LOOP
33 DBMS_OUTPUT.put_line('Error: ' || i ||
34 ' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
35 ' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
36 END LOOP;
37 END;
38 END;
39 /
Inside exception
Number of failures: 2
Error: 1 Array Index: 50 Message: ORA-01400: cannot insert NULL into ()
Error: 2 Array Index: 51 Message: ORA-01400: cannot insert NULL into ()
PL/SQL procedure successfully completed.
SQL>
SQL> select count(*) from exception_test;
COUNT(*)
----------
98
所以,你看到SAVE EXCEPTIONS
有两个错误。
您的测试用例的问题在于,代码永远不会进入EXCEPTION
块。您可以尝试删除RAISE_APPLICATION_ERROR
并查看。 PL / SQL块将正常执行。由于触发事件而引发的错误是不 24381
,因此代码永远不会进入异常块。
答案 1 :(得分:0)
你可以尝试下面的内容,你可以捕获INSTEAD OF TRIGGER中引发的异常
declare
TYPE t_tab IS TABLE OF exception_test_v%ROWTYPE;
l_tab t_tab := t_tab();
l_error_count NUMBER;
ex_dml_errors EXCEPTION;
ex_trigger_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(ex_trigger_errors, -20815);
PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);
BEGIN
-- Fill the collection.
FOR i IN 1 .. 100 LOOP
l_tab.extend;
l_tab(l_tab.last).id := i;
END LOOP;
-- Cause a failure.
l_tab(50).id := NULL;
l_tab(51).id := NULL;
EXECUTE IMMEDIATE 'TRUNCATE TABLE exception_test';
-- Perform a bulk operation.
BEGIN
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO exception_test_v (id)
VALUES (l_tab(i).id);
EXCEPTION
WHEN ex_dml_errors THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
WHEN ex_trigger_errors THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index captured in instead of trigger: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message captured in instead of trigger: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
END;