簡體   English   中英

數據庫視圖上的Oracle 11gR2 FORALL SAVE EXCEPTIONS無效

[英]Oracle 11gR2 FORALL SAVE EXCEPTIONS on database view not working

我想利用Oracle批量DML操作和異常處理。 我的要求是在數據庫視圖上執行DML操作,在該視圖中它通過視圖觸發器執行一些驗證,然后最終插入/更新基礎表。 但是,Oracle的FORALL .. SAVE EXCEPTIONS似乎沒有捕獲視圖中引發的驗證錯誤。 這是SAVE EXCEPTION的限制/限制,它僅適用於數據庫表而不適用於視圖嗎? 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上的錯誤以進行后檢查。

任何對此的反饋將不勝感激!

立即忘記INSTEAD OF觸發器。 讓我們專注於BULK EXCEPTIONS部分。 我的測試用例有一個IDNOT NULL列的表。 一個看法。 我將使用FORALL INSERT並嘗試通過集合中的索引50和51將NULL值插入到VIEW中。 期望在嘗試在VIEW插入NULL時獲得EXCEPTION

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 ,因此代碼永遠不會進入異常塊。

您可以嘗試以下類似的方法來捕獲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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM