Oracle nocopy方法

use*_*608 6 oracle procedure

我创建了一个Oracle块来检查在关联数组上使用nocopy的效果; 创建一个包含1000000个元素的数组,并将其作为参数传递给两个相同的方法,第一次作为输出参数,第二次作为输出nocopy.代码如下所示:

declare
type my_type is table of varchar2(32767) index by binary_integer;
my_array my_type;
st number;
rt number;
procedure in_out(m1 in out my_type)
is
begin
dbms_output.put_line(my_array(1));
end in_out;
procedure in_out_nocopy(m1  in out nocopy my_type)
is
begin
dbms_output.put_line(my_array(1));
end in_out_nocopy;
begin
for i in 1..999999 loop
  my_array(i) := '123456789012345678901234567890123456789012345678901234567890abcd';
end loop;
st := dbms_utility.get_time;
in_out(my_array);
rt := (dbms_utility.get_time - st)/100;
dbms_output.put_line('Time needed for in out is: ' || rt || ' 100''ths of second!');
st := dbms_utility.get_time;
in_out_nocopy(my_array);
rt := (dbms_utility.get_time - st)/100;
dbms_output.put_line('Time needed for in out nocopy is: ' || rt || ' 100''ths of second!');
end;
Run Code Online (Sandbox Code Playgroud)

现在,这将报告nocopy方法在.27秒后表现更好.我对两件事情感到困惑:

i)如果我改变两种方法的主体

begin
null;
end; 
Run Code Online (Sandbox Code Playgroud)

没有时间差异,但参数传递的差异仍然存在.为什么会这样?

ii)如果我将程序机构保持为

begin
null;
end;
Run Code Online (Sandbox Code Playgroud)

而这次不是将参数定义为out in in out nocopy,而是将其定义为out out out nocopy我确实得到了时间差.我认为参数是重新初始化的,所以为什么我在这里得到一个时间差而不是在外面的情况下呢?

此致,克里斯托斯

Vin*_*rat 7

不错的测试用例,我得到了与Oracle 11gR1(11.1.0.7.0)相同的结果.

这是文档所说的内容NOCOPY:

NOCOPY提示(在"NOCOPY"中描述).

默认情况下,PL/SQL按值传递OUT和IN OUT子程序参数.在运行子程序之前,PL/SQL将每个OUT和IN OUT参数复制到临时变量,该变量在子程序执行期间保存参数的值.如果子程序正常退出,则PL/SQL将临时变量的值复制到相应的实际参数.如果子程序退出时出现未处理的异常,则PL/SQL不会更改实际参数的值.

当OUT或IN OUT参数表示大型数据结构(如集合,记录和ADT实例)时,复制它们会降低执行速度并增加内存使用量 - 尤其是对于ADT实例.

对于ADT方法的每次调用,PL/SQL都会复制ADT的每个属性.如果方法正常退出,则PL/SQL将应用该方法对属性所做的任何更改.如果退出方法时出现未处理的异常,则PL/SQL不会更改属性.

如果子程序以未处理的异常结束,程序不要求OUT或IN OUT参数保留其预调用值,则在参数声明中包含NOCOPY提示.NOCOPY提示请求(但不确保)编译器通过引用而不是值传递相应的实际参数.

请注意,NOCOPY这仅仅是一个提示,(即不是命令).有些情况下不会受到尊重.

无论如何,NOCOPY的行为是case(1)和(3)的标准(是的,PL/SQL将在出错时恢复OUT参数的值).(2)怎么样?

我认为NULL例程在情况(2)中被优化掉了.让我们尝试关闭优化:

SQL> alter session set plsql_optimize_level=0;

Session altered

SQL> DECLARE
  2     TYPE my_type IS TABLE OF LONG INDEX BY BINARY_INTEGER;
  3     my_array my_type;
  4     st       NUMBER;
  5     rt       NUMBER;
  6     PROCEDURE in_out(m1 IN OUT my_type) IS
  7     BEGIN
  8        NULL;--dbms_output.put_line(my_array(1));
  9     END in_out;
 10     PROCEDURE in_out_nocopy(m1 IN OUT NOCOPY my_type) IS
 11     BEGIN
 12        NULL;--dbms_output.put_line(my_array(1));
 13     END in_out_nocopy;
 14  BEGIN
 15     FOR i IN 1 .. 9999999 LOOP
 16        my_array(i) :=
 17         '123456789012345678901234567890123456789012345678901234567890abcd';
 18     END LOOP;
 19     st := dbms_utility.get_time;
 20     in_out(my_array);
 21     rt := (dbms_utility.get_time - st) / 100;
 22     dbms_output.put_line('Time needed for in out is: '
 23                          || rt || ' seconds!');
 24     st := dbms_utility.get_time;
 25     in_out_nocopy(my_array);
 26     rt := (dbms_utility.get_time - st) / 100;
 27     dbms_output.put_line('Time needed for in out nocopy is: '
 28                          || rt || ' seconds!');
 29  END;
 30  /

Time needed for in out is: 5,59 seconds!
Time needed for in out nocopy is: 0 seconds!
Run Code Online (Sandbox Code Playgroud)

正如所料,差异神奇地再次出现:)