必须显式写LIMIT,因为BULK COLLECT默认不限行数,会一次性将全表数据加载至PGA内存,极易触发ORA-04030导致OOM崩溃;典型安全值为100–1000行/批,含LOB时应压至100以下。
不加 limit 的 bulk collect 很容易把 pga 帐户刷爆,不是慢,是直接崩——ora-04030 出现前往往没任何预警。
为什么必须显式写 LIMIT,不能靠默认行为
BULK COLLECT 默认不限行数,它会把整个结果集一次性加载进 PGA 内存。查一张 50 万行、每行平均 1.2KB 的表,就是约 600MB PGA 占用,远超多数实例的单会话 PGA 限制(通常 200–500MB)。这不是“性能差”,是资源耗尽导致会话被杀。
-
LIMIT必须写在FETCH语句末尾,比如FETCH c_emp BULK COLLECT INTO l_tab LIMIT 1000;它不能放在SELECT或OPEN里 - 别信“我这台服务器内存大”,RAC 环境下 PGA 是共享的,一个游标吃光内存会影响其他所有会话
- 典型安全值:100–1000 行/批;含
CLOB或BLOB字段时,建议压到 100 以下
集合类型选错会导致 FORALL 失效或报 ORA-06502
不是所有集合都能传给 FORALL。VARRAY 长度固定,容易溢出;INDEX BY VARCHAR2 下标不连续,FORALL i IN 1..v_tab.COUNT 会跳过空位漏数据;而纯 INDEX BY PLS_INTEGER 在某些 DML 场景下不支持绑定。
- 优先声明为
TYPE t_tab IS TABLE OF your_table%ROWTYPE(嵌套表),这是最通用稳妥的选择 - 如果只更新部分字段,别用
%ROWTYPE,改用显式字段集合:TYPE t_ids IS TABLE OF employees.employee_id%TYPE,再SELECT emp_id BULK COLLECT INTO v_ids FROM ... -
BULK COLLECT INTO的目标变量必须是集合类型,不能是标量或记录变量;类型和 SELECT 字段顺序、精度必须严格匹配
怎么判断游标取完了?别信 %NOTFOUND
%NOTFOUND 在 BULK COLLECT 后不可靠。比如最后一批只取到 1 行,%NOTFOUND 仍是 FALSE;只有再 FETCH 一次才会变 TRUE——这会导致多一次空 fetch,浪费 IO 且可能触发异常。
- 正确退出条件是
EXIT WHEN l_tab.COUNT = 0,每次FETCH后立刻检查集合长度 - 如果集合为空,
l_tab.COUNT返回 0,不会报错,可安全用于判断 - 别在
FORALL前清空集合用l_tab := t_tab(),要用l_tab.DELETE,否则旧数据残留导致逻辑错误
FORALL 更新时怎么知道哪几条没生效
FORALL 默认只要一条失败,整批回滚。加 SAVE EXCEPTIONS 只是“延迟报错”,不查 SQL%BULK_EXCEPTIONS 就等于没捕获。
- 必须紧跟
FORALL后用EXCEPTION WHEN OTHERS THEN包裹,并检查SQL%BULK_EXCEPTIONS.COUNT > 0 - 遍历时用
SQL%BULK_EXCEPTIONS(i).ERROR_INDEX对应回原始集合下标,不是数组索引 - 拼错误信息时记得加负号:
SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE),漏负号返回空字符串 - 别忽略
SQL%BULK_ROWCOUNT:某条UPDATE的WHERE条件不匹配时,SQL%BULK_ROWCOUNT(i)返回 0,但不会报错
最常被忽略的是分页粒度与单行体积的耦合关系——LIMIT 设成 1000 没问题,但若某列是 CLOB,实际内存占用可能翻 10 倍。上线前必须用真实数据跑一次 V$PGA_TARGET_ADVICE 查峰值。



















