I need to insert some values into a table from a collection in a procedure, but I'm getting an ORA-00902: invalid datatype
error.
This is a target table:
create table t_test (col01 number, col02 number);
I'm defining a type of collection and a pipelined function in a package:
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;
And this is my PL/SQL procedure:
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;
Resulting table would look something like this:
col01 | col02
-------|-------
69 | 10
69 | 20
69 | 30
And this is the error I'm getting:
What am I doing wrong? How to fix it? I could've done this in a for
cycle, but isn't it too inefficient for required purpose?
The locally defined PL/SQL collection types cannot be used within a non-query DML statement (eg as an argument to a table function). Just move the initialization of the test data to the table function. Consider the following example:
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
If the arguments of the collection type are essential, use either the FORALL
statement, or the SQL data types as suggested in the @XING answer .
Demo with the package as in the question without any change:
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
What am I doing wrong? How to fix it? I could've done this in a for cycle, but isn't it too inefficient for required purpose?
If you inspect the error properly you could see the error. The error says:
Local Collection Types are not allowed in SQL statement
Which means in your execution block:
insert into T_TEST(col01, col02) select n_num, column_value from table(tn_test);
Above statement is NOT ALLOWED
.
Until Oracle 11g, you cannot use a Type
declared under the scope if PLSQL
block directly under SQL
statement used inside the block. You need to change the scope of declaration of Type
outside the PLSQL
scope. Which means, you need to REMOVE
type t_num is table of number;
from the package specification and create a TYPE
outside the in SQL
scope.
So you can do this:
Create or replace type t_num is table of number;
See below demo:
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;
Execution:
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;
Test:
SQL> Select * from T_TEST;
COL01 COL02
---------- ----------
69 10
69 20
69 30
Because there's a type mismatch for that INSERT
statement among col02
and column_value
, which are of type number
and one-dimensional array
, respectively. If an iteration is applied to that array, the individual numbers are derived for each iteration step. It can be managed through use of a 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;
That way, you'll get
select * from t_test;
+------+------+
|col01 |col02 |
+------+------+
| 69 | 10 |
| 69 | 20 |
| 69 | 30 |
+------+------+
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.