FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT 如何指向引发异常的集合元素?

How does FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT point to the elements of the collection that threw the exceptions?

我已将此代码作为 :

CREATE OR REPLACE PROCEDURE PROC1 (V_EMP_ID DBMS_SQL.NUMBER_TABLE)
IS
    lv_error_string VARCHAR2(4000);
BEGIN
    FORALL INDX IN V_EMP_ID.FIRST..V_EMP_ID.LAST SAVE EXCEPTIONS
    UPDATE EMPLOYEES 
     ---trying to rasie an exception by using a calculation
    SET SALARY=SALARY * 999999999999
    WHERE ID_E= V_EMP_ID(INDX);

EXCEPTION
    WHEN OTHERS 
    THEN
    FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT
    LOOP
        ---Am printing the value of the exception array.
        dbms_output.put_line('exception Raised for record' ||V_EMP_ID(i));           
    END LOOP;
END;
/

谁能解释一下 V_EMP_ID(i) 如何指向导致异常的实际元素,因为 i1 .. SQL%BULK_EXCEPTIONS.COUNT 中。 V_EMP_ID(i) 不应该在第一次迭代中始终指向集合的第一个元素吗?


PS 于 12 月 17 日添加了以下内容

在我下面的代码中,出于某种原因,我试图在不使用 MERGE INTO 的情况下模拟 MERGE INTO 功能。

使用集合,我将记录从临时 table 插入到主 table。如果该行存在于主 table 中,我们将得到 DUP_VAL_ON_INDEX 类错误,并使用 SAVE EXCEPTIONS.
保存错误详细信息 在 EXCEPTION 块中,我想更新所有出错的主要 table 行,并使用临时 table.
中的一组值更新它们 令人惊讶的是,FORALL i IN 1 .. SQL%BULK_EXCEPTIONS.COUNTtemp_data(i) 为我更新了主要 table 中的正确行。我也尝试了非连续行并且效果很好。这怎么可能?

declare
TYPE data_tbl IS TABLE OF EMPLOYEE_TEMP%rowtype INDEX BY PLS_INTEGER;
    temp_data data_tbl;
begin       
        select * bulk collect into temp_data from EMPLOYEE_TEMP
        where EVENT_OID='30047767_1' and USERID='SINISDI2';
    
   FORALL i IN 1 .. temp_data.COUNT SAVE EXCEPTIONS
       INSERT INTO EMPLOYEE D
        (D.VPD_KEY,
              D.OID,
              D.EVENT_OID,
              D.PID,
              D.AMOUNT,
              D.MIR,
              D.REPORTING,
              D.MODIFIED_BY_USER,
              D.MODIFIED_ON,
              D.VERSION,
              D.TYPE_OID,
              D.F_ELIGIBLE)
       VALUES ( temp_data(i).VPD_KEY,
              temp_data(i).OID,
              temp_data(i).EVENT_OID,
              temp_data(i).PID,
              temp_data(i).AMOUNT,
              temp_data(i).MIR,
              temp_data(i).REPORTING,
              temp_data(i).USERID,
              SYSDATE,
              0,
              temp_data(i).TYPE_OID,
              temp_data(i).F_ELIGIBLE
              );
  
   EXCEPTION
   WHEN OTHERS
   THEN
      IF SQLCODE = -24381
      THEN
    FORALL i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT 
   UPDATE EMPLOYEE D
     SET      D.AMOUNT=temp_data(i).AMOUNT,
              D.REPORTING=temp_data(i).REPORTING,
              D.MIR=temp_data(i).MIR,
              D.MODIFIED_BY_USER=temp_data(i).USERID,
              D.MODIFIED_ON = temp_data(i).MODIFIED_ON,
              D.VERSION = D.VERSION+1,
              D.F_ELIGIBLE = S.F_ELIGIBLE
       where D.PID = temp_data(i).PID AND D.TYPE_OID = temp_data(i).TYPE_OID;
      ELSE 
         DBMS_OUTPUT.PUT_LINE(SQLERRM(SQLCODE)) ;
      END IF;     
end; 
/  

引自首都12.4.1.4 Handling FORALL Exceptions After FORALL Statement Completes:

SQL%BULK_EXCEPTIONS(i).ERROR_INDEX is the number of the DML statement that failed.

DML语句的编号相当于集合中的元素索引

看看下面的工作示例:

create table tab (id, val check (val<=3)) as
    select rownum, rownum from dual connect by level<=3
/
declare 
    type idtab is table of int;
    ids idtab;
begin    
    select id bulk collect into ids
    from tab;
    
    declare
        forallexcp exception;
        pragma exception_init (forallexcp, -24381);
    begin
        forall i in 1..ids.count save exceptions
            update tab set val = val + 1
            where id = ids(i); 
    exception when forallexcp then
        dbms_output.put_line (
            sql%rowcount||' row(s) inserted ('||sql%bulk_exceptions.count||' with error).');    
        for i in 1..sql%bulk_exceptions.count loop dbms_output.put_line (
            'id='||ids(sql%bulk_exceptions(i).error_index)||' sqlerrm='||
            sqlerrm (-(sql%bulk_exceptions(i).error_code)));
        end loop;
    end;
end;
/

结果:

2 row(s) inserted (1 with error).
id=3 sqlerrm=ORA-02290: check constraint (.) violated