Конвейерная функция не возвращает таблицу с ошибками - PullRequest
0 голосов
/ 01 мая 2019

Мне нужно вставить некоторые значения в таблицу из коллекции в процедуре, но я получаю ошибку ORA-00902: invalid datatype.

Это целевая таблица:

create table t_test (col01 number, col02 number);

Я определяю тип коллекции и конвейерную функцию в пакете:

create or replace package p_test is 
  type t_num is table of number;
  function rtn(arg_tn t_num) return t_num PIPELINED;
end p_test;
/
create or replace package body p_test is 
  function rtn(arg_tn t_num) return t_num PIPELINED is 
      tn_row number;
    begin 
      for i in arg_tn.first .. arg_tn.last loop
        tn_row := arg_tn(i);
        pipe row(tn_row);
      end loop;
      return;
  end;
end p_test;

И это моя процедура PL / SQL:

declare
  tn_test p_test.t_num := p_test.t_num(10,20,30);
  n_num number := 69;
begin
  insert into T_TEST(col01, col02) select n_num, column_value from table(tn_test);
end;

Результирующая таблица будет выглядеть примерно такэто:

 col01 | col02
-------|-------
 69    | 10
 69    | 20
 69    | 30

И вот эта ошибка, которую я получаю:

errors 6550, 642, 306

Что я делаю не так?Как это исправить?Я мог бы сделать это в цикле for, но не слишком ли он неэффективен для требуемой цели?

Ответы [ 3 ]

2 голосов
/ 07 мая 2019

Локально определенные типы коллекций PL / SQL не могут использоваться внутри оператора DML без запроса (например, в качестве аргумента для табличной функции). Просто переместите инициализацию тестовых данных в табличную функцию. Рассмотрим следующий пример:

create or replace package p_test is 
    type t_num is table of number;
    function rtn return t_num pipelined;
end p_test;
/
create or replace package body p_test is 
    function rtn return t_num pipelined is 
        tn_test t_num := t_num (10, 20, 30);
    begin 
        for i in 1..tn_test.count loop
            pipe row (tn_test (i));
        end loop;
        return;
    end;
end p_test;
/

begin
    insert into t_test (col01, col02) 
    select rownum, column_value from table (p_test.rtn ())
    ;
    dbms_output.put_line (sql%rowcount||' rows inserted.');  
end;
/
select * from t_test;     

3 rows inserted.

     COL01      COL02
---------- ----------
         1         10
         2         20
         3         30

Если аргументы типа коллекции являются существенными, используйте либо оператор FORALL, либо типы данных SQL, как предложено в ответе @ XING .

Демо с пакетом как в вопросе без изменений:

declare 
    sources p_test.t_num := p_test.t_num (10,20,30);
    targets p_test.t_num; 
    retrows p_test.t_num; 
    n_num number := 69;
begin
    select * bulk collect into targets
    from table (p_test.rtn (sources))
    ;
    forall i in indices of targets
    insert into t_test (col01, col02) values (n_num, targets (i)) 
    returning col01 bulk collect into retrows
    ;
    dbms_output.put_line (retrows.count||' rows inserted.'); 
end;
/

3 rows inserted.

select * from t_test;

     COL01      COL02
---------- ----------
        69         10
        69         20
        69         30
1 голос
/ 02 мая 2019

Что я делаю не так? Как это исправить? Я мог бы сделать это в течение цикл, но не слишком ли он неэффективен для требуемой цели?

Если вы правильно проверите ошибку, вы можете увидеть ее. Ошибка говорит:

Локальные типы коллекций не допускаются в операторе SQL

Что означает в вашем блоке выполнения:

вставить в T_TEST (col01, col02) выбрать n_num, column_value из стол (tn_test); * +1014 *

Выше указано NOT ALLOWED.

До Oracle 11g вы не можете использовать Type, объявленный в области действия, если PLSQL блок непосредственно в операторе SQL, используемом внутри блока. Вам необходимо изменить область объявления Type за пределами области действия PLSQL. Это означает, что вам нужно REMOVE

type t_num is table of number; из спецификации пакета и создайте TYPE вне области действия SQL.

Итак, вы можете сделать это:

Create or replace type t_num is table of number; 

См. Демоверсию ниже:

create table t_test (col01 number, col02 number);

-- Moving the type decalration under the scope of SQL.
Create or replace type t_num is table of number;

create or replace package p_test is
--  type t_num is table of number; --<-- Commenting the declaration since this is not allowed until 11g.
  function rtn(arg_tn t_num) 
    return t_num  PIPELINED;
end p_test;
/

create or replace package body p_test is
  function rtn(arg_tn t_num) 
    return t_num PIPELINED 
   is
    tn_row number;
  begin
    for i in arg_tn.first .. arg_tn.last
    loop
      tn_row := arg_tn(i);
      pipe row(tn_row);
    end loop;
    return;
  end;
end p_test;

Исполнение:

declare
  tn_test t_num := t_num(10, 20, 30);
  n_num   number := 69;
begin
  insert into T_TEST
    (col01,
     col02)
    select n_num,
           column_value
    from   table(tn_test);
    commit;
end;

Тест:

 SQL> Select * from T_TEST;

     COL01      COL02
---------- ----------
        69         10
        69         20
        69         30
0 голосов
/ 01 мая 2019

Поскольку существует несоответствие типов для этого оператора INSERT среди col02 и column_value, которые имеют тип number и one-dimensional array соответственно. Если итерация применяется к этому массиву, отдельные числа выводятся для каждого шага итерации. Им можно управлять с помощью cursor:

declare
  tn_test p_test.t_num := p_test.t_num(10,20,30);
  n_num number := 69;
begin
  for c in ( select row_number() over (order by 0) rn, column_value from table(tn_test) t)
  loop
    insert into t_test(col01,col02) values(n_num, tn_test(c.rn));
  end loop;
end;

Таким образом, вы получите

select * from t_test;

+------+------+
|col01 |col02 |
+------+------+
|  69  | 10   |
|  69  | 20   |
|  69  | 30   |
+------+------+
...