PL/SQL批量收集到具有稀疏键的关联数组

Dan*_*Dan 11 oracle plsql associative-array

我想在PL/SQL中执行SQL查询,并将结果填充到关联数组中,其中SQL中的一列成为关联数组中的键.例如,假设我有一个Person带有列的表

PERSON_ID   INTEGER      PRIMARY KEY
PERSON_NAME VARCHAR2(50)
Run Code Online (Sandbox Code Playgroud)

...和价值观如下:

 PERSON_ID  |  PERSON_NAME
 ------------------------
 6          |  Alice
 15         |  Bob
 1234       |  Carol
Run Code Online (Sandbox Code Playgroud)

我想批量收集此表TABLE OF VARCHAR2(50) INDEX BY INTEGER,使得6此关联数组中的键具有值Alice等等.可以在PL/SQL中完成吗?如果是这样,怎么样?

Rob*_*cke 14

不,您必须使用2个集合(id,name)或其元素类型为记录的集合.

以下是后者的示例:

  cursor getPersonsCursor is
    SELECT ID, Name
    FROM   Persons
    WHERE  ...;

  subtype TPerson is getPersonsCursor%rowtype;
  type TPersonList is table of TPerson;
  persons TPersonList;
begin

open getPersonsCursor;
fetch getPersonsCursor
  bulk collect into persons;
close getPersonsCursor;

if persons.Count > 0 then
  for i in persons.First .. persons.Last loop
    yourAssocArray(persons(i).ID) := persons(i).Name;
  end loop;
end if;
Run Code Online (Sandbox Code Playgroud)


APC*_*APC 6

如果我们想在关联数组的索引中指定值,那么我们必须使用以下语法:

SQL> declare
  2      type n_array is table of varchar2(30)
  3           index by binary_integer;
  4      emp_names n_array;
  5  begin
  6      for r in ( select ename, empno from emp )
  7      loop
  8          emp_names(r.empno) := r.ename;
  9      end loop;
 10
 11      dbms_output.put_line('count='||emp_names.count()
 12                               ||'::last='||emp_names.last());
 13      dbms_output.put_line(emp_names(8085));
 14
 15  end;
 16  /
count=19::last=8085
TRICHLER

PL/SQL procedure successfully completed.

SQL>
Run Code Online (Sandbox Code Playgroud)

我们可以使用批量收集填充关联数组,但仅当索引是整数时,我们很乐意通过(隐式)ROWNUM索引,即不是稀疏键...

SQL> declare
  2      type n_array is table of varchar2(30)
  3           index by binary_integer;
  4      emp_names n_array;
  5  begin
  6      select ename
  7      bulk collect into emp_names
  8      from emp ;
  9
 10      dbms_output.put_line('count='||emp_names.count()
 11                               ||'::last='||emp_names.last());
 12      dbms_output.put_line(emp_names(19));
 13
 14  end;
 15  /
count=19::last=19
FEUERSTEIN

PL/SQL procedure successfully completed.

SQL>
Run Code Online (Sandbox Code Playgroud)

公平地说,如果你需要使用BULK COLLECT,你可能正在处理的数据多于适用于关联数组的数据.

编辑

两种方法的廉价性能测试:

SQL> declare
  2      type n_array is table of varchar2(30)
  3           index by binary_integer;
  4      emp_names n_array;
  5      s_time pls_integer;
  6      e_time pls_integer;
  7  begin
  8      s_time := dbms_utility.get_time;
  9      select ename
 10      bulk collect into emp_names
 11      from big_emp
 12      where rownum <= 500;
 13      dbms_output.put_line('bulk collect elapsed time = '
 14                              ||to_char(dbms_utility.get_time - s_time));
 15      s_time := dbms_utility.get_time;
 16      for r in ( select ename, empno from big_emp
 17                 where rownum <= 500 )
 18      loop
 19          emp_names(r.empno) := r.ename;
 20      end loop;
 21      dbms_output.put_line('sparse array elapsed time = '
 22                              ||to_char(dbms_utility.get_time - s_time));
 23  end;
 24  /

bulk collect elapsed time = 0
sparse array elapsed time = 0

PL/SQL procedure successfully completed.

SQL>
Run Code Online (Sandbox Code Playgroud)

众所周知,挂钟性能测试非常复杂.但是对于几百条记录,任何差异都不值得担心,当然在我们可能想要使用相关阵列的地方.

编辑2

@Dan说:

在我看来,想要查询一个相当大的行数到一个可用于恒定时间查找的数据结构应该是一个非常普遍的需要

这实际上取决于你对"一个体面的数字"的定义.是否真的存在许多情况,我们希望使用字符串索引填充具有数千行的关联数组?当我们得到这些数字时,普通的数据库表可能同样有用,特别是在带有结果集缓存的11g企业版上.

  • 正如FEUERSTEIN自己建议的那样,除非你处理少量的行(数百个),否则永远不要使用游标FOR循环.(或者你根本不会对性能表示不满) (2认同)