web-dev-qa-db-ja.com

PL / SQLのnull連想配列のこのチェックが失敗するのはなぜですか?

テーブル列のrowtypeのタイプによって作成された連想配列があります。

例を挙げると、次のようになります(テーブル名は異なりますが、構造は同じです)。

これはテーブルのDDLです

CREATE TABLE employees
  (
     id     NUMBER,
     name   VARCHAR2(240),
     salary NUMBER
  ); 

これが私の手順が行っていることです:

DECLARE
    TYPE table_of_emp
      IS TABLE OF employees%ROWTYPE INDEX BY BINARY_INTEGER;
    emp TABLE_OF_EMP;
BEGIN
    IF emp IS NULL THEN
      dbms_output.Put_line('Null associative array');
    ELSE
      dbms_output.Put_line('Not null');
    END IF;
END; 

これはshould結果として「Null連想配列」が出力されると思います。ただし、if条件は失敗し、実行はelse部分にジャンプします。

ここで、コレクション値を出力するためにforループを挿入すると

DECLARE
    TYPE table_of_emp
      IS TABLE OF employees%ROWTYPE INDEX BY BINARY_INTEGER;
    emp TABLE_OF_EMP;
BEGIN
    IF emp IS NULL THEN
      dbms_output.Put_line('Null associative array');
    ELSE
      dbms_output.Put_line('Not null');

      FOR i IN emp.first..emp.last LOOP
          dbms_output.Put_line('Emp name: '
                               || Emp(i).name);
      END LOOP;
    END IF;
END; 

次に、プログラムユニットは、forループ行を参照して例外を発生させます。

ORA-06502:PL/SQL:数値または値のエラー

これは、nullの連想配列が原因であると私は推測します。 null連想配列が原因でエラーが発生していますか?

では、なぜ最初のチェックが失敗するのでしょうか。私は何が間違っているのですか?

データベースサーバーはOracle11g EE(バージョン11.2.0.3.0 64ビット)です。

13
Sathyajith Bhat

これにより「Null連想配列」が出力されるはずだと思います。この仮定は連想配列では間違っています。それらは宣言されたときに存在しますが、空です。他のタイプのPL/SQLコレクションの場合は正しいでしょう。

初期化するまで、ネストされたテーブルまたはvarrayはアトミックにnullです。コレクション自体はnullであり、その要素ではありません。ネストされたテーブルまたはVARRAYを初期化するには、コレクションタイプと同じ名前のシステム定義関数であるコンストラクターを使用します。この関数は、渡された要素からコレクションを構築します。

各VARRAYおよびネストされたテーブル変数のコンストラクターを明示的に呼び出す必要があります。 3番目の種類のコレクションである連想配列は、コンストラクターを使用しません。コンストラクター呼び出しは、関数呼び出しが許可されている場所であればどこでも許可されます。 コレクションの初期化と参照

比較:

SQL> declare
  2      type varchar2_100_aa is table of varchar2(100) index by binary_integer;
  3      test varchar2_100_aa;
  4  begin
  5      test(1) := 'Hello';
  6      dbms_output.put_line(test(1));
  7  end;
  8  /
Hello

PL/SQL procedure successfully completed.

SQL> declare
  2      type varchar2_100_va is varray(100) of varchar2(100);
  3      test varchar2_100_va;
  4  begin
  5      test(1) := 'Hello';
  6      dbms_output.put_line(test(1));
  7  end;
  8  /
declare
*
ERROR at line 1:
ORA-06531: Reference to uninitialized collection
ORA-06512: at line 5

変数配列が正しく実行されました:

SQL> declare
  2      type varchar2_100_va is varray(10) of varchar2(100);
  3      test varchar2_100_va;
  4  begin
  5      test := varchar2_100_va(); -- not needed on associative array
  6      test.extend; -- not needed on associative array
  7      test(1) := 'Hello';
  8      dbms_output.put_line(test(1));
  9  end;
 10  /
Hello

PL/SQL procedure successfully completed.

連想配列が空であるため、firstlastはnullです。これが、2番目の例がORA-06502: PL/SQL: Numeric or value errorになる理由です。

SQL> declare
  2      type varchar2_100_aa is table of varchar2(100) index by binary_integer;
  3      test varchar2_100_aa;
  4  begin
  5      dbms_output.put_line(test.count);
  6      dbms_output.put_line(coalesce(to_char(test.first), 'NULL'));
  7      dbms_output.put_line(coalesce(to_char(test.last), 'NULL'));
  8      test(1) := 'Hello';
  9      dbms_output.new_line;
 10      dbms_output.put_line(test.count);
 11      dbms_output.put_line(coalesce(to_char(test.first), 'NULL'));
 12      dbms_output.put_line(coalesce(to_char(test.last), 'NULL'));
 13  end;
 14  /
0
NULL
NULL

1
1
1

PL/SQL procedure successfully completed.

[〜#〜] edit [〜#〜]連想配列はスパースである可能性があることにも注意してください。 firstlastの間の数値をループすると、まばらなコレクションに対して例外が発生します。代わりに、firstnext を次のように使用します:(Lastprevを使用して、反対方向をループします。)

SQL> declare
  2      type varchar2_100_aa is table of varchar2(100) index by binary_integer;
  3      test varchar2_100_aa;
  4      i binary_integer;
  5  begin
  6      test(1) := 'Hello';
  7      test(100) := 'Good bye';
  8      dbms_output.put_line(test.count);
  9      dbms_output.put_line(coalesce(to_char(test.first), 'NULL'));
 10      dbms_output.put_line(coalesce(to_char(test.last), 'NULL'));
 11      dbms_output.new_line;
 12  --
 13      i := test.first;
 14      while (i is not null) loop
 15          dbms_output.put_line(to_char(i, '999')  || ' - ' || test(i));
 16          i := test.next(i);
 17      end loop;
 18  end;
 19  /
2
1
100

   1 - Hello
 100 - Good bye

PL/SQL procedure successfully completed.
13

最初のチェックが失敗する理由についてはお答えしません。私はそのようなことをすることを考えたことがなく、それがエラーを引き起こさないことに非常に驚いています。

ループで例外が発生する理由は、ご指摘のとおり、インデックス_emp.first_が存在しないためです。

Nullをチェックするのではなく、実際にこのインデックスの存在をチェックする必要があります。 .exists(i)構文を使用して実行できること:

_if not emp.exists(emp.first) then
   dbms_output.put_line('Nothing in here.');
end if;
_
5
Ben