Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oracle 11gR2 for数据库视图上的所有保存异常不工作_Oracle_View_Plsql_Exception Handling_Bulkinsert - Fatal编程技术网

Oracle 11gR2 for数据库视图上的所有保存异常不工作

Oracle 11gR2 for数据库视图上的所有保存异常不工作,oracle,view,plsql,exception-handling,bulkinsert,Oracle,View,Plsql,Exception Handling,Bulkinsert,我想利用Oracle批量DML操作和异常处理。我的要求是在数据库视图上执行DML操作,通过视图触发器执行一些验证,然后最后插入/更新底层表。然而,甲骨文的福尔。。保存异常似乎无法捕获视图中引发的验证错误。这是否是saveexception的限制/限制,它只处理数据库表而不处理视图?Oracle文档似乎也没有提到这一点。以下是我的测试代码(基于来自的修改): 创建表: create table exception_test ( id number(10) not null ); 在表上创建

我想利用Oracle批量DML操作和异常处理。我的要求是在数据库视图上执行DML操作,通过视图触发器执行一些验证,然后最后插入/更新底层表。然而,甲骨文的福尔。。保存异常似乎无法捕获视图中引发的验证错误。这是否是saveexception的限制/限制,它只处理数据库表而不处理视图?Oracle文档似乎也没有提到这一点。以下是我的测试代码(基于来自的修改):

创建表:

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上的错误以进行后期检查


对此的任何反馈都将不胜感激

现在就忘掉
而不是
触发器。让我们关注
批量异常部分。我的测试用例有一个表,其
ID
列为
notnull
列。对它的看法。我将使用
进行所有插入
,并尝试通过集合中的索引50和51将
NULL
值插入
视图
。当尝试在
视图中插入
NULL
时,期望得到
异常

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\u APPLICATION\u ERROR
并查看。PL/SQL块将正常执行。由于触发事件而引发的错误是而不是
24381
,因此代码永远不会进入异常块。

您是否可以尝试以下方法,在其中捕获在中引发的异常而不是触发器

  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;

您能否在视图中执行单个测试插入并检查是否引发异常???是的,使用上面的我的测试代码,引发的异常是触发器中的
ORA-20815
,而不是
ORA-24381
中的
SAVE EXCEPTIONS
。因此,我无法批量捕获异常,因为
FORALL
操作将立即出错。请检查此表的XXX\u updateable\u列(XXX可以是ALL,USER或DBA)以查看ID列是否可更新。从这个答案中可以看出,该列是可更新的,因此不需要触发器,谢谢大家的回复。我需要在这里使用instead of触发器,因为我需要在将数据插入表之前执行一系列验证。我的原始帖子中的
raise\u application\u error
只是一个示例,我想测试一下,看看它是否可以捕获到
SAVE EXCEPTION
。但似乎
SAVE EXCEPTION
实际上只适用于与直接DML操作相关的错误(?)。我试图寻找Oracle文档来确认我的假设,但仍然没有成功。谢谢,我尝试过使用
,而其他人则使用
,错误在
for all INSERT
期间被捕获在索引50上,但是循环只是停在这里,而没有继续执行
INSERT的其余部分
,甚至指定了
SAVE EXCEPTIONS
。此外,如果未引发异常
ex\u dml\u errors-24381
,则使用
SAVE EXCEPTIONS
没有意义,因为不再批量处理错误。