PL/SQL密集计算应避免循环中调用SQL函数、VARCHAR2超长拼接、NULL隐式传播、循环内聚合及临时LOB未释放。须改用PL/SQL原生运算、CLOB流式拼接、NVL显式处理、SQL层聚合下推、成对管理临时LOB。

PL/SQL里做密集数学计算,别在循环里反复调用SQL函数
Oracle的SQRT、POWER、LOG等标量函数在SQL层调用开销远高于PL/SQL原生运算。如果在FOR i IN 1..100000 LOOP里写SELECT SQRT(x) INTO v_res FROM DUAL,每次都会触发SQL引擎解析+上下文切换,实测慢3–5倍。
实操建议:
- 纯数值计算一律用PL/SQL内置运算符:
v_res := SQRT(v_x)(注意:这是PL/SQL自带的函数,不是SQL层的) - 避免在循环中执行
SELECT ... INTO——哪怕只查DUAL,也改用直接赋值 - 若必须复用复杂公式,封装成
FUNCTION并加DETERMINISTIC,但仅限无副作用、参数全为标量的场景 - 确认是否真需要高精度:
NUMBER类型运算比BINARY_DOUBLE慢,对科学计算可显式转为BINARY_DOUBLE提速
拼接超4000字节字符串必崩,||不是万能解法
||拼接在Oracle里是“类型陷阱”重灾区:只要结果超4000字节(单字节字符集),或任一操作数是CLOB,立刻报ORA-06502或ORA-22835。这不是写法错,是VARCHAR2硬限制+隐式转换规则导致的。
实操建议:
- 明确长度预期:若可能超4000,从一开始就把目标变量声明为
CLOB - 禁用
str := str || part模式——每次拼接都新建VARCHAR2对象,内存翻倍增长 - 改用
DBMS_LOB.WRITEAPPEND流式追加:DBMS_LOB.CREATETEMPORARY(l_clob, TRUE)→ 循环中WRITEAPPEND→ 最后DBMS_LOB.FREETEMPORARY(l_clob) - NULL值必须显式处理:
NVL(col, ''),否则'a' || NULL || 'b'结果是NULL,逻辑静默失效
批量聚合别在PL/SQL循环里SUM/COUNT
在游标循环中写SELECT SUM(x) INTO v_sum FROM t WHERE id = :i是最典型反模式。1000次循环=1000次硬解析+1000次索引查找+1000次上下文切换,CPU和逻辑读爆炸。
实操建议:
- 聚合必须下推到SQL层:用
SELECT SUM(x) FROM t WHERE id IN (SELECT COLUMN_VALUE FROM TABLE(my_id_array))一次性算完 - 数组超1000元素时,改用全局临时表(GTT)+
JOIN,避免IN子句膨胀 - 只想判断是否存在数据?用
SELECT 1 FROM t WHERE ... AND ROWNUM = 1配%FOUND,比COUNT(*) > 0少读全部匹配行 - 别滥用
RESULT_CACHE:带绑定变量的聚合查询缓存键变化频繁,反而占共享池还失效快;静态报表才考虑
临时LOB不配对FREETEMPORARY,问题会缓慢恶化
DBMS_LOB.CREATETEMPORARY分配的是PGA内存,漏掉FREETEMPORARY不会立即报错,但内存持续累积。下次执行可能因PGA不足直接失败,且错误现象分散(如ORA-04030、慢得离谱、甚至会话卡死)。
最容易被忽略的点:
- 所有
CREATETEMPORARY必须与FREETEMPORARY成对出现,且放在EXCEPTION块里确保异常时也能释放 - 目标
CLOB变量初始化必须非NULL,否则WRITEAPPEND会报错 - 拼接源是
VARCHAR2可直传;若是CLOB,需先DBMS_LOB.CREATETEMPORARY再COPY或APPEND


















