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)
如何指向导致异常的实际元素,因为i
在1 .. 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.COUNT
和 temp_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循环[i*j for i in range for j in range]