为什么PL / SQL中的空关联数组检查失败?

我有一个由表列的行类型创建的关联数组 .

举个例子,它是这样的(表名不同,但结构是一样的):

这是表的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;

我认为这应该导致"Null associative array"被打印 . 但是, 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关联数组而引发错误?

那么为什么第一次检查失败呢?我究竟做错了什么?

数据库服务器是Oracle 11g EE(版本11.2.0.3.0 64位)

回答(2)

3 years ago

我认为这应该导致"Null associative array"被打印 . 这种假设对于关联数组是错误的 . 它们在声明时存在,但是是空的 . 对于其他类型的PL / SQL集合,这是正确的:

在初始化之前,嵌套表或varray在原子上为null;集合本身是null,而不是它的元素 . 要初始化嵌套表或varray,请使用构造函数,这是一个与集合类型同名的系统定义函数 . 此函数根据传递给它的元素构造集合 . 您必须为每个varray和嵌套表变量显式调用构造函数 . 关联数组是第三种集合,不使用构造函数 . 只要允许函数调用,就允许构造函数调用 . 初始化和引用集合

相比:

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 为空,这就是为什么你的第二个例子导致 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.

3 years ago

我不打算回答为什么第一次检查失败 . 我从来没有想过做这样的事情,我很惊讶它没有引起错误 .

你're getting an exception raised on the loop is, as you'注意到,索引 emp.first 不存在的原因 .

您应该检查是否存在此索引,而不是检查空值 . 你可以使用 .exists(i) 语法:

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