Подтвердить что ты не робот

Почему эта проверка для нулевого ассоциативного массива в PL/SQL не выполняется?

У меня есть ассоциативный массив, созданный типом 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; 

Я предполагаю, что это должно привести к печати "Нулевой ассоциативный массив". Однако условие 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; 

тогда программный блок вызывает исключение, ссылаясь на строку цикла

ORA-06502: PL/SQL: числовая или значная ошибка

который, как я полагаю, обусловлен нулевым ассоциативным массивом. Возникает ли ошибка из-за нулевого ассоциативного массива?

Так почему же первый сбой проверки? Что я делаю неправильно?

Сервер базы данных - это Oracle 11g EE (версия 11.2.0.3.0 64 бит)

4b9b3361

Ответ 1

Я предполагаю, что это должно привести к печати "Нулевой ассоциативный массив". Это предположение неверно для ассоциативных массивов. Они существуют, когда объявляются, но пусты. Это было бы правильно для других типов коллекций PL/SQL:

Пока вы не инициализируете его, вложенная таблица или varray атомарно нуль; сама коллекция является нулевой, а не ее элементами. Чтобы инициализировать вложенной таблицы или varray, вы используете конструктор, определяемый системой с тем же именем, что и тип коллекции. Эта функция создает коллекции из переданных ему элементов.

Вы должны явно вызвать конструктор для каждого varray и вложенного переменная таблицы. Ассоциативные массивы, третий тип коллекции, do не использовать конструкторы. Конструкторские вызовы разрешены везде, где функция звонки разрешены. Инициализация и ссылки на коллекции

Для сравнения:

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.

Поскольку ассоциативный массив пуст first и last равны нулю, вот почему ваш второй пример приводит к 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 Также обратите внимание, что ассоциативные массивы могут быть разрежены. Цикл между номерами first и last приведет к возникновению исключения для любой редкой коллекции. Вместо этого используйте first и next следующим образом: (last и prev для петли в другом направлении.)

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.

Ответ 2

Я не буду отвечать, почему первая проверка не работает. Я никогда не думал о том, чтобы что-то делать, и я очень удивлен, что это не вызывает ошибки.

Причина, по которой вы получаете исключение, поднятое в цикле, - это, как вы уже отмечали, индекс emp.first не существует.

Вместо проверки нулей, вы действительно должны проверить наличие этого индекса. Что вы можете сделать, используя синтаксис .exists(i):

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