FORALL是Oracle对批量DML的底层SQL引擎优化机制,非简单循环加速;必须确保所有绑定集合COUNT严格一致、批量大小控制在500~2000、关闭AUTOCOMMIT、正确处理SAVE EXCEPTIONS错误索引,且不支持表达式、函数或动态SQL。

FORALL 不是“更快的 for 循环”,它是 Oracle 对批量 DML 的底层 SQL 引擎优化机制;用错集合对齐、盲目调大批量或混用动态赋值,性能反而比逐行还差。
FORALL 绑定集合 COUNT 必须严格一致
Oracle 不校验逻辑对齐,只按索引位置硬绑定。一旦 a_arr.COUNT ≠ b_arr.COUNT,立刻报 ORA-06512。常见错误包括:
- 一个集合用
EXTEND动态追加,另一个用固定下标(如b_arr(5) := 'xxx')导致稀疏空位 -
FETCH BULK COLLECT时LIMIT超出剩余行数,但后续未检查l_ids.COUNT就直接进FORALL - 用了
INDICES OF idx_list,但其他集合没做对应映射,造成错位
实操建议:
- 每次
FETCH后立即加DBMS_OUTPUT.PUT_LINE('ids:'||l_ids.COUNT||' names:'||l_names.COUNT) - 填充集合统一用同一循环:
FOR i IN 1..n LOOP a_arr.EXTEND; a_arr(i):=...; b_arr.EXTEND; b_arr(i):=...; END LOOP
批量大小控制在 500~2000 是性能甜点区
设太大(如 LIMIT 10000)会导致 PGA 内存暴涨、临时段写入频繁、游标失效,尤其当目标表有 3+ 索引或触发器时,优势迅速归零。19c 隐式分片优化依赖可控的单次绑定量。
- OLTP 场景推荐
LIMIT 500~2000,实测吞吐最稳 - 超 5000 易触发临时段写入或游标失效,别迷信“越大越好”
- 若绑定数组长度超 10000,19c 会自动拆为子批,但每批仍需单独硬解析——复杂 DML 应主动分段,避免隐式分片开销
SAVE EXCEPTIONS 下 ERROR_INDEX 不是源数组下标
启用 SAVE EXCEPTIONS 后,SQL%BULK_EXCEPTIONS(i).ERROR_INDEX 返回的是 FORALL 内部执行序号(从 1 开始),不是你原始集合的物理下标。直接拿它去查 id_arr 会取错记录。
- 未用
INDICES OF时:正确写法是failed_id := id_arr(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX) - 用了
INDICES OF idx_list时:需再映射一层orig_idx := idx_list(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX); failed_id := id_arr(orig_idx) - 取错误码必须用
SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE);单独调用SQLERRM只返回最后一条
FORALL 不支持表达式、函数或条件分支
FORALL 只接受静态 SQL,所有绑定变量必须是集合元素直引。下面都是非法写法:
-
FORALL i IN 1..arr.COUNT UPDATE t SET name = UPPER(arr2(i)) WHERE id = arr(i)→ 报ORA-06550 -
CASE WHEN ... THEN ...、子查询、@dblink全部不支持
正确做法:预处理集合,例如 arr2_upper(i) := UPPER(arr2(i)),再在 FORALL 中直引 arr2_upper(i)。
最容易被忽略的是空集合处理和 AUTOCOMMIT —— 19c 中 FORALL 遇到空集合可能触发 PLW-07203 警告(尤其 CI 流水线),而默认开启的 AUTOCOMMIT 会让每次 FORALL 后自动提交,彻底退化为单条执行。



















