6

我创建了一个 Oracle 块来检查使用 nocopy 对关联数组的影响;创建一个包含 1000000 个元素的数组并将其作为参数传递给两个相同的方法,第一次作为 in out 参数,第二个作为 in out 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 方法的性能提高了 0.27 秒。我对两件事感到困惑:

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

begin
null;
end; 

不会注意到时间差异,但是参数传递的差异仍然存在。为什么会这样?

ii)如果我将程序主体保留为

begin
null;
end;

这一次,我没有将参数定义为 in out 和 in out nocopy,而是将其定义为 out 和 out nocopy,我确实得到了时间差。我想出参数无论如何都会重新初始化,那么为什么我在这里得到一个时差而不是在 out case 中呢?

问候,克里斯托斯

4

1 回答 1

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 的行为对于情况 (1) 和 (3) 是标准的(是的,PL/SQL 将在出错的情况下恢复 OUT 参数的值)。(2)呢?

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

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!

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

于 2013-03-07T14:32:56.907 回答