BULK COLLECT必须用集合类型接收,标量变量会报PLS-00497;分批FETCH需带LIMIT(推荐500–5000);FORALL前须校验集合非空且长度一致;BULK COLLECT不抛NO_DATA_FOUND,须用.COUNT判断空结果。

PL/SQL中BULK COLLECT必须用集合类型接收,标量变量直接报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,且位置不能错
大数据量下不加LIMIT就等于裸奔:FETCH cur BULK COLLECT INTO tab可能一次加载百万行,触发ORA-04030(PGA耗尽)或拖慢整个实例。
LIMIT值要权衡:
- 太小(如
LIMIT 10):上下文切换没减多少,循环次数反而飙升 - 太大(如
LIMIT 100000):单次内存压力大,GC频繁,容易被ORA-04030拦截 - 经验值:500–5000行之间较稳,具体看单行平均字节数和PGA限制
关键细节:LIMIT必须写在FETCH cur BULK COLLECT INTO tab 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。
这个点容易被忽略——尤其在循环分批处理时,最后一轮可能没取到数据,但程序仍继续执行FORALL,导致空集合报错或逻辑跳过。最稳妥的做法是每次BULK COLLECT后立即加IF tab.COUNT = 0 THEN EXIT; END IF;。


















