ERROR_INDEX返回的是FORALL内部执行序号(从1开始),非源数组物理下标;若用INDICES OF或VALUES OF,须通过映射层转换为原数组下标,否则取值错误。
启用 SAVE EXCEPTIONS 后,SQL%BULK_EXCEPTIONS(i).ERROR_INDEX 返回的是 FORALL 内部执行序号(从 1 开始),不是你源集合的物理下标。直接用它查 id_arr 会取错记录。
常见错误写法:failed_id := id_arr(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX); ——仅在 FORALL i IN 1..n 这种连续下标场景下才碰巧正确。
INDICES OF idx_list,必须再映射一层:orig_idx := idx_list(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX); failed_id := id_arr(orig_idx);
VALUES OF pos_list,同样要先用 pos_list(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX) 得到原始位置DBMS_OUTPUT.PUT_LINE('err idx:'||SQL%BULK_EXCEPTIONS(i).ERROR_INDEX||' orig:'||orig_idx); 能快速验证映射是否对齐ORA-06512 本身是堆栈跟踪错误,真正触发它的通常是 ORA-06502(数值/字符错误)或 ORA-01408(重复索引),根源几乎全是参与 FORALL 的多个集合 COUNT 不等。
FORALL 不校验逻辑对齐,只按索引位置硬绑定——哪怕 b_arr(5) 是 NULL 或未初始化,Oracle 仍会尝试传入空值或触发连锁错误。
FOR i IN 1..n 循环赋值,避免一个用 EXTEND、另一个用固定下标DBMS_OUTPUT.PUT_LINE('a:'||a_arr.COUNT||' b:'||b_arr.COUNT);,比看错误堆栈快得多INDICES OF:它跳过稀疏空位,但其他集合不会自动对齐,极易错位把 BULK COLLECT INTO ... LIMIT 设成 10000 甚至 50000,常导致 PGA 暴涨、GC 频繁,实际吞吐下降。Oracle 12c+ 的隐式分片优化依赖可控的单次绑定量。
目标表有 3+ 索引或触发器时,FORALL 优势衰减极快,此时盲目调大批量毫无意义。
LIMIT 500~2000,实测为性能甜点区AUTOCOMMIT,否则每次 FORALL 后隐式提交,等于退化成单条插入FORALL 只接受静态 SQL,所有绑定变量必须是集合元素直引,不能带函数、CASE、子查询或 DBLink。
错误写法:FORALL i IN 1..arr.COUNT INSERT INTO t VALUES (arr(i), UPPER(arr2(i))); → 触发 ORA-06550。
arr2_upper(i) := UPPER(arr2(i));,再进 FORALLIF 判断后选择不同 INSERT),需拆成多个 FORALL 块