NOCOPY仅在传大型集合且子程序只读或局部修改时才可能省时间;对标量参数无效,异常时可能导致实参被部分改写,且受表达式传参、复杂类型、调试模式等限制常被忽略。
什么时候加 NOCOPY 真能省时间?
只在传大型集合(比如 t_string_list、sys.odcivarchar2list、含几十字段的 record)且子程序内部只读或仅做局部修改时,nocopy 才可能减少拷贝开销。标量参数(number、date、varchar2(100))加了完全无效——oracle 本来就是按指针传 in,而 out/in out 标量拷贝成本微乎其微。
常见误判场景:
- 以为“只要参数大就该加”,结果传的是
IN参数:NOCOPY对IN参数被 Oracle 忽略,白写 - 过程里对参数做了
DELETE、TRIM、EXTEND全量操作,实参内存被直接改写,调用方数据意外丢失 - 没确认瓶颈:
BULK COLLECT一个 10 万行数组进出过程,拷贝耗时可能就 0.5ms,但若整个过程耗时 2s,优化它毫无意义
NOCOPY 为什么经常不生效?
Oracle 把 NOCOPY 当作提示(hint),不是强制指令。以下情况它会悄悄退回到拷贝模式:
- 调用时传的是表达式,比如
my_proc(my_array || 'x')、my_proc(1 + 2)或字面量my_proc('hello')——没法取地址,必须拷贝 - 参数类型是
%ROWTYPE或复杂%TYPE(如字段含CLOB、XMLTYPE),Oracle 认为引用传递风险高,主动放弃 - 当前会话开启了调试:
ALTER SESSION SET PLSQL_DEBUG = TRUE,所有NOCOPY自动失效 - 参数声明用了
CONSTANT或绑定到常量变量,编译器判定不可变,绕过引用逻辑
怎么写才不会改掉调用方的数据?
核心原则:别假设参数是“干净副本”。哪怕你加了 NOCOPY,上游也可能已经用它传进来——你一清空,对方数据就没了。
安全写法:
- 需要重置内容时,显式创建本地副本:
l_local_copy t_string_list := p_list;,后续操作只动l_local_copy - 输出前校验:如果过程要返回新集合,先
p_list.DELETE再EXTEND+ 赋值,但前提是文档明确约定“该参数仅用于输出”且调用方知情 - 避免混用:
IN OUT NOCOPY参数别既当输入又当输出容器;拆成IN+OUT两个参数更清晰
错误示例:p_list.DELETE 直接写在过程开头,没隔离——调用方传进来的数组可能已被清空。
外部调用时 NOCOPY 还管用吗?
不管用。NOCOPY 只影响 PL/SQL 引擎内部的参数传递行为,对外部程序(JDBC、ODP.NET、Python cx_Oracle)完全透明。这些客户端传参走的是 SQL*Net 协议序列化,和 NOCOPY 无关。
真正影响外部性能的是:
- 参数个数:每多一个绑定变量,网络包和解析开销都增加
- 参数大小:传一个 1MB 的
CLOB比传 10 个NUMBER慢得多,但这和NOCOPY无关 - 类型映射:用
OBJECT TYPE封装多个字段传入,比 10+ 个扁平参数更高效,但需提前注册类型,且NOCOPY对它不生效
所以别指望加了 NOCOPY 就能让 Java 调用变快——那是另一层优化的事。


















