【问题标题】:Why is this check for null associative array in PL/SQL failing?为什么检查 PL/SQL 中的空关联数组失败?
【发布时间】:2012-09-06 21:06:02
【问题描述】:

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

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

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

我认为这应该会导致打印“空关联数组”。但是,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:数字或值错误

我认为是因为空关联数组。是否因为空关联数组而引发错误?

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

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

【问题讨论】:

  • 我有一段时间没做plsql了,但是在你的for循环中(dbms_output.Put_line('Emp name: ' || Emp(i).name);)应该Emp(i).nameemp(i).name吗?
  • @jschoen 变量名不区分大小写,所以没关系

标签: oracle plsql oracle11g associative-array


【解决方案1】:

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

在你初始化它之前,嵌套表varray是原子性的空; 集合本身是空的,而不是它的元素。初始化一个 嵌套表或可变数组,您使用构造函数,系统定义的 与集合类型同名的函数。这个功能 从传递给它的元素构造集合。

您必须为每个可变数组显式调用构造函数并嵌套 表变量。 关联数组,第三种集合,做 不使用构造函数。任何函数都允许构造函数调用 允许通话。 Initializing and Referencing Collections

比较:

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.

【讨论】:

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

我不会回答为什么第一次检查失败。我从来没有想过要做这样的事情,并且很惊讶它不会引发错误。

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

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

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

【讨论】:

  • Ben,Oracle 文档说“您不能将 EXISTS 与关联数组一起使用。” - docs.oracle.com/cd/B28359_01/appdev.111/b28370/… 话虽如此,程序单元运行成功。小时。
  • 是的@Sathya,抱歉错过了您是从表格行类型执行此操作的事实。它运行成功吗?对我不好吗?如果这是游标的行类型,它将起作用。
  • 是的,当我编辑它时,奇怪的是它运行成功,尽管文档另有说明
  • 最奇怪的I can't get it work with a table rowtype 根据您的原始评论; with a cursor 很简单。我假设是你,你的最小例子是正确的:-)。
  • 为什么不简单地使用 if emp.count = 0 then null 否则它不会起作用
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-18
  • 1970-01-01
  • 1970-01-01
  • 2021-03-06
  • 2011-05-24
相关资源
最近更新 更多