web-dev-qa-db-ja.com

PL / SQLはスパースキーを使用して連想配列に一括収集します

PL/SQL内でSQLクエリを実行し、結果を連想配列に入力します。ここで、SQLの列の1つが連想配列のキーになります。たとえば、列のあるテーブルPersonがあるとします。

_PERSON_ID   INTEGER      PRIMARY KEY
PERSON_NAME VARCHAR2(50)
_

...そして次のような値:

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

この連想配列のキー_6_の値がAliceになるように、このテーブルをTABLE OF VARCHAR2(50) INDEX BY INTEGERに一括収集したいと思います。これはPL/SQLで実行できますか?もしそうなら、どのように?

11
Dan

いいえ、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;
14
Robert Giesecke

連想配列のインデックスに値を指定する場合は、次の構文を使用する必要があります。

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>

can連想配列に一括収集を設定しますが、インデックスが整数の場合に限り、(暗黙の)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を使用する必要がある場合は、連想配列に適しているよりも多くのデータを処理している可能性があります。

編集

2つのアプローチの安価なパフォーマンステスト:

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

@ダンは言った:

一定時間のルックアップに使用できるデータ構造に適切なサイズの行をクエリしたいというのは、かなり一般的なニーズであると私には思えます。

それは本当にあなたの「まともなサイズの数」の定義に依存します。 連想配列に数千行、文字列インデックスを入力したい場合は本当にたくさんありますか?これらの種類の数値に到達すると、特に 11g Enterprise Editionと結果セットのキャッシュ では、通常のデータベーステーブルも同様に役立つ可能性があります。

6
APC