BULK COLLECT INTO 必须用集合类型接收,标量变量会报PLS-00497错误;因SQL引擎返回多值集合,PL/SQL必须用能容纳“一组”的容器(如TABLE OF、%ROWTYPE)接收,且需声明即初始化并校验COUNT以避免ORA-06531等异常。

BULK COLLECT INTO 必须用集合类型接收,标量变量直接报 PLS-00497
这是最常踩的坑:写 SELECT col BULK COLLECT INTO v_val FROM t,而 v_val 是 NUMBER 或 VARCHAR2 这类标量,Oracle 立刻抛 PLS-00497: cannot mix between single row and multi-row (BULK) operations。错误不是语法错,是类型契约强制——SQL 引擎返回的是多值集合,PL/SQL 只能用能装“一组”的容器接。
必须提前声明集合类型,常见三种方式:
- 查单列、纯字符串/数字:用
TABLE OF VARCHAR2(100) INDEX BY PLS_INTEGER(关联数组),支持稀疏索引,不用预分配,适合后续按 key 查找 - 查多列、需保持字段结构:先定义
RECORD,再声明TABLE OF record_type(嵌套表),访问时用tab(i).col1 - 查整行、表结构可能变:用
%ROWTYPE声明嵌套表,如TYPE emp_tab IS TABLE OF employees%ROWTYPE,但注意会拉全字段,内存开销大
游标分批 FETCH 时 LIMIT 不可省,位置必须在 FETCH 行末尾
大数据量下不加 LIMIT 就等于裸奔:FETCH cur BULK COLLECT INTO tab 可能一次加载百万行,触发 ORA-04030(PGA 耗尽)或拖慢整个实例。
LIMIT 值要权衡:
- 太小(如
LIMIT 10):上下文切换没减多少,循环次数反而飙升 - 太大(如
LIMIT 100000):单次内存压力大,GC 频繁,容易被ORA-04030拦截 - 经验值:500–5000 行之间较稳,具体看单行平均字节数和 PGA 限制
关键细节:LIMIT 必须写在 FETCH ... BULK COLLECT INTO ... LIMIT n 这一行里,不能放在 OPEN 后或单独成句。
FORALL 批量 DML 前必须校验集合长度一致且非空
用 FORALL i IN l_ids.FIRST .. l_ids.LAST INSERT INTO t VALUES (l_ids(i), l_names(i)) 时,如果 l_ids 和 l_names 长度不同,或某次 BULK COLLECT 后集合为空,FORALL 直接报 ORA-22160 或下标越界。
必须做三件事:
- 每次
BULK COLLECT后,用.COUNT显式校验所有参与FORALL的集合长度是否一致 - 空集合要跳过
FORALL块,否则i IN NULL..NULL会出错 - 统一用
1..COUNT而非FIRST..LAST,尤其对稀疏关联数组,前者更安全
SELECT BULK COLLECT INTO 不抛 NO_DATA_FOUND,要用 COUNT 判断结果为空
这点和普通 SELECT INTO 完全不同:SELECT * BULK COLLECT INTO tab FROM t WHERE 1=0 不会触发异常,tab.COUNT 返回 0。所以不能靠异常捕获来判断无数据,必须显式检查 tab.COUNT = 0。
另外,BULK COLLECT 本身不初始化集合,若声明时未赋初值(如 tab my_tab; 而非 tab my_tab := my_tab();),首次 COUNT 可能报 ORA-06531(引用未初始化集合)。最稳妥写法是声明即初始化。
真正难的不是语法,而是集合生命周期管理——什么时候清空、要不要重用、空集合怎么跳过、内存峰值怎么控。这些细节不处理好,批量操作反而比逐行还慢。

















