NOCOPY是优化提示而非强制指令,当参数为NOT NULL+DEFAULT的复合类型、传入字面量、启用DETERMINISTIC/调试模式或存在“写后读”时会被忽略;其生效与否需通过变量值变化、性能剖析或执行计划间接验证。

PL/SQL中NOCOPY为什么没生效?
NOCOPY不是强制指令,而是优化提示——Oracle 仅在满足特定条件时才真正避免值复制。一旦触发任何一条“否决条件”,它就自动退化为默认的按值传递(by value),你看到的“依然复制”不是bug,是预期行为。
哪些情况会让NOCOPY被忽略?
常见但容易被忽视的失效场景包括:
- 参数类型是
RECORD或TABLE等复合类型,且其中某个字段是NOT NULL约束 +DEFAULT表达式(哪怕只是DEFAULT SYSDATE) - 调用方传入的是常量、字面量或表达式(如
my_proc('hello' || 'world')),而非变量名 - 子程序被声明为
DETERMINISTIC或PARALLEL_ENABLE,Oracle为保证语义安全主动禁用NOCOPY - 调试模式启用(
ALTER SESSION SET PLSQL_DEBUG=TRUE),所有NOCOPY提示临时失效 - 参数在过程体内被赋值后又读取(即“写后读”),Oracle无法确保别名安全性,回退到复制
NOCOPY对性能影响有多大?
影响取决于参数大小和调用频次:
- 小对象(如
VARCHAR2(100)、NUMBER):复制开销几乎可忽略,NOCOPY是否生效无感 - 大集合(如
DBMS_SQL.VARCHAR2_TABLE含上万行):一次复制可能多出几MB内存+毫秒级CPU,高频调用下可观测到延迟上升 - 注意:即使
NOCOPY生效,也不能假设调用方变量会被修改——只有当过程内明确赋值给该参数,且未被优化掉时,才可能反映回传
怎么确认NOCOPY到底有没有起作用?
没有直接SQL查“是否用了NOCOPY”,但可通过间接方式验证:
- 在过程内修改
IN OUT参数,然后检查调用方变量是否变化(需关闭优化:ALTER SESSION SET PLSQL_OPTIMIZE_LEVEL=0) - 用
DBMS_PROFILER或DBMS_HPROF对比开启/关闭NOCOPY时的内存分配峰值 - 最可靠的是看执行计划中的
PL/SQL block部分是否出现copy动作标记(需12c+,且开启DBMS_OUTPUT.ENABLE配合trace)
实际中,NOCOPY的“不可靠性”恰恰是它的设计哲学:宁可保守复制,也不冒险破坏数据一致性。真正需要极致控制内存的场景,往往得绕开PL/SQL参数传递本身,改用全局临时表或DBMS_SQL动态绑定。











