问题描述
我注意到在pl/sql中使用关联数组时会产生奇怪的副作用。基本上,它看起来,当数组的元素作为 “in out nocopy” 传递给过程时,然后在过程完成后,Oracle可能会将更新的元素复制回关联数组。问题是,Oracle在调用过程之前使用包含键的变量。一切都很好,直到程序也改变了那个键。
我已经提供了代码作为LiveSQL链接,但以防万一也包括在这里:
上述代码产生
因此,在程序完成后,预计键为 “2017-01” 的记录的 “bis” 等于2017-01-15,而键为 “2017-01-15” 的新记录的 “von” 等于2017-01-15。在程序结束之前就是这样。程序结束后,将交换记录。
我的问题是,这是一个错误,还是一个功能?换句话说,这应该得到修复还是记录在案?
我已经提供了代码作为LiveSQL链接,但以防万一也包括在这里:
--%<---%<---%<---
declare
type tr_zs_info is record(
von varchar2(10),
bis varchar2(10)
);
type th_zs_set is table of tr_zs_info index by varchar2(10);
l_key varchar2(10);
l_other varchar2(10);
l_hash th_zs_set;
procedure split_period(
pl_el in out nocopy tr_zs_info,
p_at varchar2,
pl_key in out nocopy varchar2,
pl_dst in out nocopy th_zs_set
) is
l_obj tr_zs_info;
begin
dbms_output.put_line('start of split von ' || pl_el.von || ' bis ' || pl_el.bis);
l_obj.von := p_at;
l_obj.bis := pl_el.bis;
pl_el.bis := p_at;
pl_dst(p_at) := l_obj;
pl_key := p_at;
dbms_output.put_line('end of split '
|| p_at || ' von ' || pl_dst(p_at).von || ' bis ' || pl_dst(p_at).bis);
end split_period; -- }}}
begin
l_key := '2017-01-01';
l_hash(l_key).von := l_key;
l_hash(l_key).bis := '2017-02-01';
l_other := l_key;
split_period(l_hash(l_key), '2017-01-15', l_key, l_hash);
dbms_output.put_line('after split '
|| l_key || ' von ' || l_hash(l_key).von || ' bis ' || l_hash(l_key).bis);
dbms_output.put_line('original '
|| l_other || ' von ' || l_hash(l_other).von || ' bis ' || l_hash(l_other).bis);
end;
--%<---%<---%<---
上述代码产生
--%<---%<---%<--- start of split von 2017-01-01 bis 2017-02-01 end of split 2017-01-15 von 2017-01-15 bis 2017-02-01 after split 2017-01-15 von 2017-01-01 bis 2017-01-15 original 2017-01-01 von 2017-01-01 bis 2017-02-01 --%<---%<---%<---
因此,在程序完成后,预计键为 “2017-01” 的记录的 “bis” 等于2017-01-15,而键为 “2017-01-15” 的新记录的 “von” 等于2017-01-15。在程序结束之前就是这样。程序结束后,将交换记录。
我的问题是,这是一个错误,还是一个功能?换句话说,这应该得到修复还是记录在案?
专家解答
这个例子没有提到,但是文档确实进入了一个 * 很多 * 的细节 (和警告),关于通过引用和值传递参数,以及nocopy和参数别名的含义。
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/plsql-subprograms.html
我对您的程序进行了一些更改,以使读者更容易理解以证明差异 (作为示例和警告)
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/plsql-subprograms.html
我对您的程序进行了一些更改,以使读者更容易理解以证明差异 (作为示例和警告)
SQL> declare
2 type my_rec is record(
3 dateidx varchar2(20),
4 data varchar2(20)
5 );
6 type my_array is table of my_rec index by varchar2(20);
7
8 l_key varchar2(10);
9 l_array my_array;
10
11 procedure print is
12 x varchar2(20);
13 begin
14 dbms_output.put_line('-------------------------');
15 dbms_output.put_line('l_key='||l_key);
16 x := l_array.first;
17 while x is not null
18 loop
19 dbms_output.put_line('IDX:'||x||' DATEIDX:'||l_array(x).dateidx||', DATA:'||l_array(x).data);
20 x := l_array.next(x);
21 end loop;
22 end;
23
24 procedure split_period(
25 pl_el in out nocopy my_rec,
26 p_at_date varchar2,
27 pl_key in out nocopy varchar2,
28 pl_dst in out nocopy my_array
29 ) is
30 l_obj my_rec;
31 begin
32 print;
33 --
34 -- create new entry with the data from existing key
35 --
36 l_obj.dateidx := p_at_date;
37 l_obj.data := pl_el.data;
38 --
39 -- update existing key with new data
40 --
41 pl_el.data := 'SOME_MORE_DATA';
42
43 --
44 -- insert new entry into the array
45 --
46 pl_dst(p_at_date) := l_obj;
47 --
48 -- last used key becomes this entry
49 --
50 pl_key := p_at_date;
51 print;
52 end split_period;
53 begin
54 l_key := '2017-01-01';
55 l_array(l_key).dateidx := l_key;
56 l_array(l_key).data := 'SOME_DATA';
57 split_period(l_array(l_key), '2017-01-15', l_key, l_array);
58 print;
59 end;
60 /
-------------------------
l_key=2017-01-01
IDX:2017-01-01 DATEIDX:2017-01-01, DATA:SOME_DATA
-------------------------
l_key=2017-01-15
IDX:2017-01-01 DATEIDX:2017-01-01, DATA:SOME_DATA
IDX:2017-01-15 DATEIDX:2017-01-15, DATA:SOME_DATA
-------------------------
l_key=2017-01-15
IDX:2017-01-01 DATEIDX:2017-01-01, DATA:SOME_DATA
IDX:2017-01-15 DATEIDX:2017-01-01, DATA:SOME_MORE_DATA
PL/SQL procedure successfully completed.
SQL>
SQL> declare
2 type my_rec is record(
3 dateidx varchar2(20),
4 data varchar2(20)
5 );
6 type my_array is table of my_rec index by varchar2(20);
7
8 l_key varchar2(10);
9 l_array my_array;
10
11 procedure print is
12 x varchar2(20);
13 begin
14 dbms_output.put_line('-------------------------');
15 dbms_output.put_line('l_key='||l_key);
16 x := l_array.first;
17 while x is not null
18 loop
19 dbms_output.put_line('IDX:'||x||' DATEIDX:'||l_array(x).dateidx||', DATA:'||l_array(x).data);
20 x := l_array.next(x);
21 end loop;
22 end;
23
24 procedure split_period(
25 pl_el in out my_rec,
26 p_at_date varchar2,
27 pl_key in out varchar2,
28 pl_dst in out my_array
29 ) is
30 l_obj my_rec;
31 begin
32 print;
33 --
34 -- create new entry with the data from existing key
35 --
36 l_obj.dateidx := p_at_date;
37 l_obj.data := pl_el.data;
38 --
39 -- update existing key with new data
40 --
41 pl_el.data := 'SOME_MORE_DATA';
42
43 --
44 -- insert new entry into the array
45 --
46 pl_dst(p_at_date) := l_obj;
47 --
48 -- last used key becomes this entry
49 --
50 pl_key := p_at_date;
51 print;
52 end split_period;
53 begin
54 l_key := '2017-01-01';
55 l_array(l_key).dateidx := l_key;
56 l_array(l_key).data := 'SOME_DATA';
57 split_period(l_array(l_key), '2017-01-15', l_key, l_array);
58 print;
59 end;
60 /
-------------------------
l_key=2017-01-01
IDX:2017-01-01 DATEIDX:2017-01-01, DATA:SOME_DATA
-------------------------
l_key=2017-01-01
IDX:2017-01-01 DATEIDX:2017-01-01, DATA:SOME_DATA
-------------------------
l_key=2017-01-15
IDX:2017-01-01 DATEIDX:2017-01-01, DATA:SOME_DATA
IDX:2017-01-15 DATEIDX:2017-01-15, DATA:SOME_DATA
PL/SQL procedure successfully completed.
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




