必须显式写limit,否则bulk collect会一次性将全表数据加载至pga导致ora-04030崩溃;limit须置于fetch末尾,典型值100–1000(含lob时≤100),配合exit when集合.count=0退出循环,并选用嵌套表类型及必要索引优化sql层。

必须显式写 LIMIT,否则 PGA 会直接打满
BULK COLLECT 默认不限行数,它会把整个结果集一次性加载进 PGA 内存。查一张 50 万行、每行平均 1.2KB 的表,就是约 600MB PGA 占用——远超多数实例单会话限制(通常 200–500MB)。这不是“慢”,是 ORA-04030 直接崩溃。
-
LIMIT只能写在FETCH语句末尾,比如FETCH c_emp BULK COLLECT INTO l_tab LIMIT 1000;不能放在SELECT或OPEN里 - 含
CLOB或BLOB字段时,LIMIT必须压到 100 以下;纯数值/字符串字段可设 500–1000 - 别信“我这台服务器内存大”——RAC 环境下 PGA 是共享的,一个游标吃光内存会影响其他所有会话
集合类型声明错,FORALL 会失效或报 ORA-06502
不是所有集合都能传给 FORALL。用错类型会导致批量逻辑退化成逐行执行,或者直接报错。
- 优先声明为
TYPE t_tab IS TABLE OF your_table%ROWTYPE(嵌套表),这是最通用稳妥的选择 - 别用
VARRAY:长度固定,容易溢出;也别用INDEX BY VARCHAR2:下标不连续,FORALL i IN 1..v_tab.COUNT会跳过空位漏数据 - 如果只更新部分字段,别用
%ROWTYPE,改用显式字段集合,比如TYPE t_ids IS TABLE OF employees.employee_id%TYPE
循环退出条件写错,多一次空 FETCH 浪费 IO
%NOTFOUND 在 BULK COLLECT 后不可靠。最后一批只取到 1 行时,%NOTFOUND 仍是 FALSE;必须再 FETCH 一次才变 TRUE——这会导致多一次空 fetch,可能触发异常。
- 正确退出条件是
EXIT WHEN l_tab.COUNT = 0,每次FETCH后立刻检查 -
l_tab.COUNT返回 0 时不会报错,可安全用于判断 - 每次循环开始前必须清空集合:
l_tab.DELETE(不是l_tab := t_tab(),后者类型稍有不匹配就报PLS-00382)
没选必要字段 + 没加索引,SQL 层就拖垮了批量
BULK COLLECT 再快,也救不了底层 SQL 走全表扫描。很多“内存高”其实是 SQL 执行慢、PGA 持续占用不释放导致的假象。
- 避免
SELECT *,只选真正需要的列;尤其避开CLOB/BLOB字段,除非业务强依赖 - 游标里加提示:比如
SELECT /*+ INDEX(a idx_status_time) */ ...,确保走索引 - WHERE 条件字段必须有索引,且不能是函数包裹(如
TRUNC(create_time)),否则索引失效 - 多表 JOIN 时,驱动表(最左表)要有高选择性过滤条件,否则中间结果集爆炸,内存照样爆
BULK COLLECT 当成银弹——它只解决 PL/SQL 层上下文切换和内存分配问题,而 SQL 执行计划、索引设计、字段选择这些底层问题,一个没调好,批量就只是“看起来快”。











