暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Oracle IN OUT打破了全球收藏的可变性?

ASKTOM 2020-05-11
241

问题描述

我在得到的一些代码中遇到了这个 “错误”,并且能够将其简化为此示例 (我只给出软件包主体,规范只是过程而已)

package body asl_test
is
  type gr_rec is record ( id number , kol varchar2(1) );
  type gt_tab is table of gr_rec index by binary_integer;

  g_tab gt_tab := gt_tab();
  
  g_const varchar2(10);

procedure modify_record 
 ( p_in in out gr_rec      --> this breaks the mutability of the global collection
 )
is  
begin
  -- This works...
  g_const := 2;
  /* 
    But the IN OUT ignores this modification
    Original code deleted the entry from the collection
    but the entry stayed.
  */
  g_tab(p_in.id).kol := 'J';
end;

procedure modify_collection
is
begin
  for i in g_tab.first .. g_tab.last
  loop
    modify_record(g_tab(i));
  end loop;
end;

procedure go
is
begin
  g_const     := 1;
  g_tab(1).id := 1; g_tab(1).kol := 'N';
  g_tab(2).id := 2; g_tab(2).kol := 'N';
  --
  modify_collection();
  --
  dbms_output.put_line('The collection is not modified!');
  for i in g_tab.first .. g_tab.last
  loop
    dbms_output.put_line(g_tab(i).kol);
  end loop;
  dbms_output.put_line('variable is correctly modified 到2: '||g_const);
end;

end;


所以我在身体中定义了一个全局集合,并用一些数据填充它。
然后迭代集合,并通过调用一个包含IN OUT参数的过程来修改每个记录 (他们有一些理由这样做)。

全局变量的修改在离开程序后保留,但是对集合的修改丢失了!

但是,当我将IN OUT更改为IN时,将保留对集合的修改。

因此,当从以下位置更改输入变量时by reference by value,我的代码中的一个不相关的变量变得不可变。

你能解释这种行为吗?

专家解答

这里的问题是代码没有为IN OUT参数分配值。事件的链条是:

-传递参数的值
-设置全局变量
-将参数的值传递出去

因为实际参数是全局变量,所以会发生这种情况:

-传递参数的值
-设置全局变量
-将参数的值传递出去 - OVERWRITING the previous assignment to the global

一个更简单的例子显示了这一点:

DECLARE
  n NUMBER := 10;

  PROCEDURE p (
    n1 IN OUT NUMBER
  ) IS
  BEGIN
    DBMS_OUTPUT.put_line('Global ' || n);   -- global value is 10
    DBMS_OUTPUT.put_line('Actual ' || n1);  -- actual parameter value is 10
    n := -99;
    DBMS_OUTPUT.put_line('Global ' || n);   -- global value is -99
    DBMS_OUTPUT.put_line('Actual ' || n1);  -- actual parameter value is still 10
      -- this value is copied back to the global when this procedure exits!
  END;

BEGIN
  p(n);
  DBMS_OUTPUT.put_line(n);
END;
/

Global 10
Actual 10
Global -99
Actual 10
10


为了避免这种情况,您需要为过程中的参数分配一个值:

DECLARE
  n NUMBER := 10;

  PROCEDURE p (
    n1 IN OUT NUMBER
  ) IS
  BEGIN
    DBMS_OUTPUT.put_line('Global ' || n);   -- global value is 10
    DBMS_OUTPUT.put_line('Actual ' || n1);  -- actual parameter value is 10
    n1 := -99;
    DBMS_OUTPUT.put_line('Global ' || n);   -- global value is 10
    DBMS_OUTPUT.put_line('Actual ' || n1);  -- actual parameter value is still -99
      -- this is copied back over the global value when this procedure exits!
  END;

BEGIN
  p(n);
  DBMS_OUTPUT.put_line(n);
END;
/

Global 10
Actual 10
Global 10
Actual -99
-99


将modify_collection中的赋值更改为:

p_in.kol := 'J';


你会得到你想要的行为。

但是传递全局参数是令人困惑的-它有点违背了使它成为一个全局的点。

最好将要更改的数组条目的索引作为IN参数传递:

procedure modify_record ( p_in in number  )
is  
begin
  g_const := 2;
  g_tab(p_in).kol := 'J';

end;

procedure modify_collection
is
begin
  for i in g_tab.first .. g_tab.last
  loop
    modify_record(i);
  end loop;
end;

文章转载自ASKTOM,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论