Oracle nocopy方法

时间:2013-03-07 14:20:39

标签: 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;

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

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

begin
null;
end; 

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

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

begin
null;
end;

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

此致 的Christos

1 个答案:

答案 0 :(得分: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仅被描述为提示(即不是命令)。有时会not be respected

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

我认为在(2)情况下NULL过程是optimized。让我们尝试关闭优化:

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!

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