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

Sat*_*hat 13 oracle plsql associative-array oracle11g

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

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

这是表的DDL

CREATE TABLE employees
  (
     id     NUMBER,
     name   VARCHAR2(240),
     salary NUMBER
  ); 
Run Code Online (Sandbox Code Playgroud)

这是我的程序正在做的事情:

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; 
Run Code Online (Sandbox Code Playgroud)

我认为这应该导致打印"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; 
Run Code Online (Sandbox Code Playgroud)

然后程序单元引发异常,引用for循环线

ORA-06502:PL/SQL:数值或值错误

我认为是因为null关联数组.是否由于null关联数组而引发错误?

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

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

Sha*_*nce 13

我认为这应该导致打印"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
Run Code Online (Sandbox Code Playgroud)

变量数组正确完成:

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.
Run Code Online (Sandbox Code Playgroud)

因为关联数组是空的first并且last为null,这就是您的第二个示例导致的结果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.
Run Code Online (Sandbox Code Playgroud)

编辑还要注意关联数组可以是稀疏的.在循环之间的数字first,并last会引发异常的任何集合,是稀疏.而是使用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.
Run Code Online (Sandbox Code Playgroud)

  • 这是正确的答案.但对我来说,它有时也突出了Oracle的怪异.当然,可变大小的"数组"(或PLSQL中的"表")的全部意义在于,您事先并不知道在执行时您将拥有多少条记录(即它很容易为零) .而使用数组的全部意义在于你可以循环使用它们!! 因此,在能够循环之前检查列表是否具有非零长度似乎完全违反直觉.为什么解析器不能简单地循环零次,就像在标准隐式游标中一样? (2认同)

Ben*_*Ben 5

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

正如您所指出的那样,您在循环中获得异常的原因是索引emp.first不存在.

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

if not emp.exists(emp.first) then
   dbms_output.put_line('Nothing in here.');
end if;
Run Code Online (Sandbox Code Playgroud)