不加LIMIT的BULK COLLECT会一次性将全部结果加载到PGA,触发ORA-04036错误;必须用显式游标+循环+LIMIT,且以EXIT WHEN collection.COUNT = 0安全退出。

为什么BULK COLLECT不加LIMIT容易触发ORA-04036
不加 LIMIT 的 BULK COLLECT 会把游标全部结果一次性加载进 PGA,尤其当查询返回几十万行时,PGA 瞬间暴涨。Oracle 12c+ 默认启用 PGA_AGGREGATE_LIMIT 硬限制,超限直接报 ORA-04036,进程中断——这不是配置疏忽,是机制本身在“保命”。
FETCH ... BULK COLLECT INTO ... LIMIT 的正确写法
必须用显式游标 + 循环 + LIMIT,且退出条件不能依赖 %FOUND,否则最后一组数据会被跳过。
关键点:
-
EXIT WHEN collection.count = 0是安全退出的唯一可靠方式 -
LIMIT值建议设为 100–1000,具体看单行数据大小;例如含 CLOB 或长 VARCHAR2 时,应下调到 100 - 集合类型推荐用
INDEX BY BINARY_INTEGER(即索引表),避免使用嵌套表导致隐式内存拷贝 - 每次循环前不用清空集合,
BULK COLLECT INTO会自动覆盖
常见错误写法及后果
这些写法看似能跑通,但生产环境极易出问题:
- 用
WHILE cursor_name%FOUND LOOP—— 第一次FETCH后 %FOUND 为 TRUE,但最后一次FETCH返回 0 行时 %FOUND 已为 FALSE,循环提前终止,漏掉最后一组数据 -
EXIT WHEN cursor_name%NOTFOUND放在FETCH之后、处理之前 —— 和上面一样,漏数据 -
LIMIT设成 10000 甚至不写 —— 单次 PGA 分配超限,尤其在并发过程多、PGA_AGGREGATE_LIMIT 设置偏紧的库上,秒报ORA-04036 - 集合类型声明为
TABLE OF ...(无INDEX BY)但没指定EXTEND—— 运行时报ORA-06531: Reference to uninitialized collection
一个可直接抄的最小安全模板
DECLARE
CURSOR c_data IS SELECT id, name FROM large_table WHERE status = 'A';
TYPE t_data IS TABLE OF c_data%ROWTYPE INDEX BY BINARY_INTEGER;
v_data t_data;
BEGIN
OPEN c_data;
LOOP
FETCH c_data BULK COLLECT INTO v_data LIMIT 500;
EXIT WHEN v_data.COUNT = 0; -- 注意:不是 %NOTFOUND
<pre class="brush:php;toolbar:false;">FOR i IN 1..v_data.COUNT LOOP
-- 处理每一行,例如插入到临时表或调用逻辑
NULL;
END LOOP;END LOOP; CLOSE c_data; END;
这个结构避开了所有典型坑:退出判断准确、LIMIT 可控、集合类型明确、无隐式内存风险。真正复杂的地方不在语法,而在于理解 %NOTFOUND 是上一次 FETCH 的状态,不是本次结果是否存在——这点被忽略一次,就可能让批量任务漏掉几千条记录。











