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

Posted

技术标签:

【中文标题】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? 【发布时间】:2020-12-16 19:00:20 【问题描述】:

我已将此代码作为example from this topic:

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 功能。

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

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; 
/  

【问题讨论】:

你是对的,这是不正确的。查看此博客文章中的代码清单 2 以获得非常详细的示例:blogs.oracle.com/oraclemagazine/on-avoiding-termination 短版,我觉得你应该用V_EMP_ID(sql%BULK_EXCEPTIONS(i).ERROR_INDEX) 【参考方案1】:

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

SQL%BULK_EXCEPTIONS(i).ERROR_INDEX 是失败的 DML 语句的编号。

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

【讨论】:

谢谢。这也是我的理解。但是你能检查我在问题中的编辑吗? 我不相信您上次编辑中的块工作正常。请在db<>fiddle 上查看此示例。我希望更新 id=(2,3) 而不是 id=(2,4)。如果您有其他数据星座,请更改 dbfiddle 并发布链接。

以上是关于FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT 如何指向引发异常的集合元素?的主要内容,如果未能解决你的问题,请参考以下文章

for-in 的坑

列表中使用嵌套for循环[i*j for i in range for j in range]

for …each…in,for in ,for of

学习这个-js迭代条件i if else for for in循环for each长度

python中的for i in range怎么用?

for in 和for of