在 PL/SQL 中将按表的索引转换为简单表的一种优雅方法,然后循环遍历每个条目

Posted

技术标签:

【中文标题】在 PL/SQL 中将按表的索引转换为简单表的一种优雅方法,然后循环遍历每个条目【英文标题】:An elegant way to transform an index by table into simple table in PL/SQL, other then loop through every entry 【发布时间】:2011-06-06 15:04:35 【问题描述】:

我有两种类型

CREATE OR REPLACE TYPE my_record_type IS OBJECT
  (
    name        varchar2(30)
  )
  ;

CREATE OR REPLACE TYPE my_table_type AS TABLE OF my_record_type

还有一个函数

create or replace my_function return my_table_type
is
  type my_hash_type is table of my_record_type index by pls_integer;
  v_hash my_hash_type;
  v_table my_table_type;
  i NUMBER;
begin
 -- some business logic here

 -- transformation part

 v_table := my_table_type();
 i := v_hash.first;

 while i is not null loop

  v_table.extend(1);
  v_table(v_table.last) := v_hash(i);
  i := v_hash.next(i); 

 end loop;

 --  end transformation part

 return v_table;
end;
/

在 10g 中是否有一种优雅的方法可以用类似的东西替换转换部分

v_table = CAST( v_hash as  my_table_type )

【问题讨论】:

看起来您并不关心v_hash 中的实际键它们自己,只关心它们的顺序。如果是这样,为什么要使用 INDEX BY 表? 如果MY_HASH_TYPEMY_RECORD_TYPE 的表,就像MY_TABLE_TYPE,为什么不将V_HASH 声明为MY_TABLE_TYPE 类型?然后你可以取消 V_TABLE 并返回 V_HASH。除非您的示例代码中缺少一些微妙的业务逻辑。 @APC:我在-- some business logic here 部分使用哈希:) 【参考方案1】:

您可以使用SELECT my_record_type(column_value) BULK COLLECT INTO v_table from table(v_hash)。但是为了使用它,您必须在函数之外创建 my_hash_type (在包规范中作为类型 OR 的支持,以便 SQL 引擎可以看到),否则您将收到PLS-00642: local collection types not allowed in SQL statements

CREATE OR REPLACE TYPE my_hash_type is table OF VARCHAR2(10);
/
set serveroutput on
declare 
 --type my_hash_type is table OF VARCHAR2(10);
  v_hash my_hash_type := my_hash_type();
  v_table my_table_type;
  i NUMBER;
begin
null ;
  for n in 60..75 loop
    V_hash.extend(1);  
    V_hash(v_hash.count) := chr(n) ;
  end loop ;

  select my_record_type(column_value)
  bulk collect into  v_table
  from table(v_hash) ;

  for n in 1..v_table.count loop
    dbms_output.put_line( n || ':>' || v_table(n).name);
  end loop ;

  --PLS-00642: local collection types not allowed in SQL statements

end ;

1:><
2:>=
3:>>
4:>?
5:>@
6:>A
7:>B
8:>C
9:>D
10:>E
11:>F
12:>G
13:>H
14:>I
15:>J
16:>K

查看here 和here 以获取更多示例等等

时间差异(based on this methodology #s 以百分之一秒为单位)

pl/sql context switch (as described above)
44
42
43
42

loop fill (with type defined outside of block) --A distinct CREATE TYPE on Oracle level

18
18
18
18

loop fill (with type defined within block) --Type created within the Anon. block
23
22
24
22

(上述时间试验是基于此代码的变化:

set serveroutput on
declare 
 --type my_hash_type  is table of my_record_type -index by pls_integer;
  v_hash my_hash_type := my_hash_type();
  v_table my_table_type;
  i NUMBER;
  time_before BINARY_INTEGER; 
  time_after BINARY_INTEGER;
begin

time_before := DBMS_UTILITY.GET_TIME; 

  for n in 0..15000 loop
    V_hash.extend(1);  
    V_hash(v_hash.count) := my_record_type(n) ;
  end loop ;


  select my_record_type(column_value)
  bulk collect into  v_table
  from table(v_hash) ;


  /*
  v_table := my_table_type();
  for n in 1..V_hash.count loop
    v_table.extend(1);
    v_table(v_table.count) := v_hash(n) ;
    --dbms_output.put_line( n || ':>' || v_table(n).name);
  end loop ;*/
  --for n in 1..v_table.count loop
  --  dbms_output.put_line( n || ':>' || v_table(n).name);
  --end loop ;
time_after := DBMS_UTILITY.GET_TIME; 

DBMS_OUTPUT.PUT_LINE (time_after - time_before);
  --PLS-00642: local collection types not allowed in SQL statements

end ;
/

因此循环填充速度提高了 50%,但时间差异仍然很小(这里是过早优化和避免某些事情之间的平衡,因为它可能太长了,我建议对您的真实数据进行时间试验以找到最适合的解决方案)。

我能想到的唯一其他“优雅”解决方案是TREAT,但您会注意到它需要一个必须在对象类型上的子类型/超类型解决方案(我无法让它在 Varray 上工作/Assoc.数组类型——希望我错了!)

【讨论】:

+1 不错的解决方案,但由于开关 plsql-> sql -> plsql 不会对性能产生影响吗? @schurik,我在 sql-pl/sql 开关和循环之间添加了一些时间差异。您会注意到循环时间减少了 50%。 感谢对我的问题的详细调查,但是 在块外/内定义的类型是什么意思? @schurik 添加了更多细节,但 type defined outside 块是指示“将类型 xyz 创建为 object();”而 w/in 块是 'DECLARE type xyz as....begin...'

以上是关于在 PL/SQL 中将按表的索引转换为简单表的一种优雅方法,然后循环遍历每个条目的主要内容,如果未能解决你的问题,请参考以下文章

数据库表的列类型转换

将数据插入 Oracle 表的最快方法是啥?

在 PL/SQL Oracle 中将 FOR 语句转换为 FORALL

oracle中一个表的行列转换

调用 Restful Web 服务和更新数据库表的 PL/SQL 过程

如何将 DEFINE 变量设置为等于 PL/SQL 中另一个已定义表的选择