Сбор данных PL/SQL в ассоциативный массив с разреженным ключом

Я хочу выполнить SQL-запрос внутри PL/SQL и заполнить результаты в ассоциативный массив, где один из столбцов SQL становится ключом в ассоциативном массиве. Например, скажем, у меня есть таблица Person со столбцами

PERSON_ID   INTEGER      PRIMARY KEY
PERSON_NAME VARCHAR2(50)

... и значения, такие как:

 PERSON_ID  |  PERSON_NAME
 ------------------------
 6          |  Alice
 15         |  Bob
 1234       |  Carol

Я хочу, чтобы массив собирал эту таблицу в TABLE OF VARCHAR2(50) INDEX BY INTEGER, так что ключ 6 в этом ассоциативном массиве имеет значение Alice и так далее. Можно ли это сделать в PL/SQL? Если да, то как?

Ответ 1

Нет, вам нужно использовать либо 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;

Ответ 2

Если мы хотим указать значение в индексе ассоциативного массива, мы должны использовать этот синтаксис:

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>

Мы можем заполнить ассоциативные массивы массивом, но только если индекс является целым числом, и мы рады индексировать (неявным) 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>

Чтобы быть справедливым, если вам нужно использовать BULK COLLECT, вы, вероятно, имеете дело с большим количеством данных, чем это подходит для ассоциативного массива.

Edit

Дешёвое тестирование производительности двух подходов:

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>

Тесты производительности настенных часов, как известно, являются канатными. Но для нескольких сотен записей любая разница вряд ли стоит беспокоиться, конечно, в контексте того места, где мы могли бы использовать ассоциативный массив.

Изменить 2

@Dan сказал:

Мне кажется, что

t хочет, чтобы запрос приличное количество строк в структуру данных, которая может быть использована для постоянный поиск должен быть довольно распространенная потребность

Это действительно зависит от вашего определения "приличного размера". Действительно ли так много случаев, когда мы хотели бы заполнить ассоциативный массив тысячами строк со строковым индексом? Когда мы дойдем до этих видов чисел, обычная таблица базы данных может быть столь же полезна, особенно на 11g Enterprise Edition с кэшированием результатов.