Как через SQL%BULK_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
        ---I'm 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) может указывать на элемент, который вызвал исключение?

Так как i берётся из 1 ... SQL%BULK_EXCEPTIONS.COUNT', то при одной ошибке V_EMP_ID(i) всегда будет указывать на первый элемент коллекции.


Свободный перевод вопроса How does FOR i IN 1 .. SQL%BULK_EXCEPTIONS.COUNT point to the elements of the collection that threw the exceptions от участника @AnuC


Ответы (1 шт):

Автор решения: 0xdb

Принятый ответ не значит, что он полность верный.

Из оф. документации кап. 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 это тоже самое, что индекс на элемент коллекции в FORALL.

Попробуйте следующий простой рабочий пример:

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

Свободный перевод ответа от участника 0xdb

→ Ссылка