Table function with bulk collection throws invalid datatype - oracle

I am writing a function and i want to wrap it with table function so i can use with select query.
Here is my type declaration and the some lines of my functions
CREATE OR REPLACE PACKAGE TYPES
AS
TYPE CURSORTYPE IS REF CURSOR;
TYPE vbugsrec
IS
RECORD (
bug_id bugs.bug_id%TYPE,
facility bugs.facility%TYPE
);
TYPE vbugstable
IS
TABLE OF vbugsrec
INDEX BY BINARY_INTEGER;
END;
/
CREATE OR REPLACE PACKAGE BODY CustomQueries
AS
FUNCTION pendverifylist (myldapid IN userpass.ldapalias%TYPE,
maxrows IN PLS_INTEGER:= CustomQueries.maxrecords)
RETURN types.vbugstable
IS
datarows types.vbugstable;
var_useralias userpass.ldapalias%TYPE
:= UPPER (pendverifylist.myldapid) ;
CURSOR pendverify_cur (
cursor_var_alias IN userpass.ldapalias%TYPE,
cursor_var_mybugstatus IN bugs.bug_status%TYPE,
cursor_var_wild IN qa_list.component%TYPE
)
IS
SELECT buglist.bug_id, buglist.facility
FROM bugs buglist,
(SELECT qa.product, qa.component
FROM qa_list qa, userpass UP
WHERE qa.qa_id = UP.userid
AND UP.ldapalias = cursor_var_alias) plist
WHERE buglist.bug_status = cursor_var_mybugstatus
AND buglist.smr_state IN (SELECT fs.finalstate
FROM finalstates fs)
AND buglist.facility = plist.product
AND (buglist.product LIKE plist.component
OR plist.component = cursor_var_wild);
BEGIN
OPEN pendverifylist.pendverify_cur (cursor_var_alias => pendverifylist.var_useralias,
cursor_var_mybugstatus => CustomQueries.default_bugstatus,
cursor_var_wild => CustomQueries.wildcard);
FETCH pendverifylist.pendverify_cur
BULK COLLECT INTO pendverifylist.datarows
LIMIT LEAST (GREATEST (0, pendverifylist.maxrows),
CustomQueries.MAXRECORDS);
CLOSE pendverifylist.pendverify_cur;
RETURN pendverifylist.datarows;
END pendverifylist;
END CustomQueries;
/
When i want to use TABLE function like below, i get error.ORA-00902: invalid datatype
SELECT * FROM TABLE(CUSTOMQUERIES.PENDVERIFYLIST ( 'product', 50 ));
Can anyone please help what i am doing wrong here?
Thanks in advance

You're trying to use package-level types in plain SQL, which isn't allowed. The types declared in the package are not visible to or valid outside PL/SQL (or even in plain SQL statements within PL/SQL). A cut-down version of what you're doing:
create or replace package types as
type my_rec_type is record (dummy dual.dummy%type);
type my_table_type is table of my_rec_type index by binary_integer;
end types;
/
create or replace package p42 as
function get_table return types.my_table_type;
end p42;
/
create or replace package body p42 as
function get_table return types.my_table_type is
my_table types.my_table_type;
begin
select * bulk collect into my_table from dual;
return my_table;
end get_table;
end p42;
/
select * from table(p42.get_table);
SQL Error: ORA-00902: invalid datatype
Even within the package, if you had a procedure that tried to use the table function it would error. If you added:
procedure test_proc is
begin
for r in (select * from table(get_table)) loop
null;
end loop;
end test_proc;
... the package body compilation would fail with ORA-22905: cannot access rows from a non-nested table item.
You need to declare the types at schema level, not in a package, so using the SQL create type command:
create type my_obj_type is object (dummy varchar2(1));
/
create type my_table_type is table of my_obj_type;
/
create or replace package p42 as
function get_table return my_table_type;
end p42;
/
create or replace package body p42 as
function get_table return my_table_type is
my_table my_table_type;
begin
select my_obj_type(dummy) bulk collect into my_table from dual;
return my_table;
end get_table;
end p42;
/
select * from table(p42.get_table);
DUMMY
-----
X

Actually tehere is not need to have types in schema level. All you need to do is to define function as PIPELINED.
-- DEFINITION IN PCKG HEADER
create or replace PACKAGE "AAA" IS
TYPE t_record IS RECORD (
aaa VARCHAR(20 CHAR),
bbb VARCHAR(50 CHAR),
ccc VARCHAR(10 CHAR)
);
TYPE t_collection is table of t_record;
FUNCTION get_records(p_in1 DATE, p_in2 DATE) RETURN t_collection PIPELINED;
END AAA;
-- PCKG BODY
create or replace PACKAGE BODY AAA AS
FUNCTION get_records(p_in1 DATE, p_in2 DATE) RETURN t_collection PIPELINED AS
CURSOR k1 is SELECT aaa,bbb,ccc FROM table;
BEGIN
FOR rec IN k1
LOOP
pipe row( (rec) );
END LOOP;
END get_records
END AAA;
-- CALLING FUNCTION OUTSIDE OF PCKG
select * from TABLE(AAA.get_records(par1, par2));

Thanks to Alex Poole. This is what i ended up with
CREATE OR REPLACE TYPE vbugsrec
IS
OBJECT (
bug_id NUMBER(9),
facility VARCHAR2(256)
);
CREATE OR REPLACE TYPE vbugstable
IS
TABLE OF vbugsrec;
/
CREATE OR REPLACE PACKAGE BODY CustomQueries
AS
FUNCTION pendverifylist (myldapid IN userpass.ldapalias%TYPE,
maxrows IN PLS_INTEGER:= CustomQueries.maxrecords)
RETURN vbugstable
IS
datarows vbugstable := vbugstable();
var_useralias userpass.ldapalias%TYPE:= UPPER (pendverifylist.myldapid) ;
TYPE temp_rec IS RECORD (
bug_id bugs.bug_id%TYPE,
facility bugs.facility%TYPE
);
TYPE temp_records
IS
TABLE OF temp_rec
INDEX BY BINARY_INTEGER;
temporary_records temp_records;
CURSOR pendverify_cur (
cursor_var_alias IN userpass.ldapalias%TYPE,
cursor_var_mybugstatus IN bugs.bug_status%TYPE,
cursor_var_wild IN qa_list.component%TYPE
)
IS
SELECT buglist.bug_id, buglist.facility
FROM bugs buglist,
(SELECT qa.product, qa.component
FROM qa_list qa, userpass UP
WHERE qa.qa_id = UP.userid
AND UP.ldapalias = cursor_var_alias) plist
WHERE buglist.bug_status = cursor_var_mybugstatus
AND buglist.smr_state IN (SELECT fs.finalstate
FROM finalstates fs)
AND buglist.facility = plist.product
AND (buglist.product LIKE plist.component
OR plist.component = cursor_var_wild);
BEGIN
OPEN pendverifylist.pendverify_cur (cursor_var_alias => pendverifylist.var_useralias,
cursor_var_mybugstatus => CustomQueries.default_bugstatus,
cursor_var_wild => CustomQueries.wildcard);
FETCH pendverifylist.pendverify_cur
BULK COLLECT INTO temporary_records
LIMIT LEAST (GREATEST (0, pendverifylist.maxrows),
CustomQueries.MAXRECORDS);
CLOSE pendverifylist.pendverify_cur;
IF temporary_records.COUNT <> 0
THEN
FOR rec_idx IN temporary_records.FIRST .. temporary_records.LAST
LOOP
datarows.EXTEND;
datarows (datarows.LAST) :=
vbugsrec (temporary_records (rec_idx).bug_id,
temporary_records (rec_idx).facility);
END LOOP;
END IF;
RETURN pendverifylist.datarows;
END pendverifylist;
END CustomQueries;
/

Related

PL/SQL - cannot access rows from a non-nested table item oracle

Can this package result be called from a procedure or function
the goal is to put that select into a procedure or function
so that when I call the procedure or function the result of the select is displayed.
This is the package i am using regarding this
create or replace PACKAGE get_cash_flow
IS
type type_rec
IS
record
(
asofdate VARCHAR2(30),
cfValue VARCHAR2(30));
type type_aa
IS
TABLE OF type_rec INDEX BY pls_integer;
FUNCTION func_aa RETURN type_aa;
END;
create or replace PACKAGE body get_cash_flow
IS
FUNCTION func_aa
RETURN type_aa
IS
l_aa_var1 get_cash_flow.type_aa;
BEGIN
FOR loop_aa IN
(
SELECT rownum rn,
asofdate, cash_inflow + TRANSFERS +ASSET_INFLOW + INTEREST_INCOME_LONG + DIVIDEND_INCOME - CASH_OUTFLOW
- ASSET_OUTFLOW-CASH_OUTFLOW -ASSET_OUTFLOW + INTEREST_INCOME_SHORT + MANAGEMENT_FEE + OTHER_FEES+EXPENSES cfValue
FROM eod_value_account_mtbl
order by asofdate
--where nf_account_id = '0208EA227E0B463ADA8BC413BA1A4D87'
--FETCH FIRST 1500000 rows only
FETCH FIRST 500 rows only
)
LOOP
l_aa_var1(loop_aa.rn).asofdate :=loop_aa.asofdate;
l_aa_var1(loop_aa.rn).cfValue:=loop_aa.cfValue;
END LOOP loop_aa;
RETURN l_aa_var1;
END func_aa;
END get_cash_flow;
here is the select I am trying to implement in a procedure or function
select *
from
get_cash_flow.func_aa();
declare
vcResult CLOB := NULL;
l_aa_var1 get_cash_flow.type_aa;
BEGIN
l_aa_var1:=get_cash_flow.func_aa;
SELECT JSON_ARRAYAGG(
JSON_OBJECT( KEY 'cfDate' VALUE asofdate,KEY 'cfValue' VALUE cfValue )
FORMAT JSON
RETURNING CLOB
) AS json
into vcResult
FROM (
SELECT asofdate,cfValue
FROM
TABLE(l_aa_var1)
);
dbms_output.put_line('vcResult: '|| vcResult);
END;
Expected result in one line data type in clob

How to append data to an output parameter which is an array?

I have a type like this ...
CREATE OR REPLACE TYPE TYPE_X
AS
TABLE OF VARCHAR2(4000);
... which is used in a package:
CREATE OR REPLACE PACKAGE PACKAGE_TEST
AS
TYPE DETAILS
IS
RECORD
(
EMPNO NUMBER,
ENAME VARCHAR2(4000),
DEPTNO NUMBER );
TYPE DETAILS_ARRAY
IS
TABLE OF DETAILS;
PROCEDURE PROC_TESTING_2(
X TYPE_X,
Y OUT DETAILS_ARRAY );
END;
CREATE OR REPLACE PACKAGE BODY PACKAGE_TEST
AS
PROCEDURE PROC_TESTING_2(
X TYPE_X,
Y OUT DETAILS_ARRAY )
AS
BEGIN
FOR I IN 1..X.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(X(I));
SELECT EMPNO,ENAME,DEPTNO INTO Y FROM EMP WHERE DEPTNO=X(I);
END LOOP;
END;
END;
I'd like to print the data into the record type by executing all the values from the TYPE_X list. The data needs to be appended till the loop exits. Appreciate your help. Thanks!!
The approach which requires the least change to your existing code would be to populate a local variable of the record type, then append that to the OUT array.
CREATE OR REPLACE PACKAGE BODY PACKAGE_TEST
AS
PROCEDURE PROC_TESTING_2(
X TYPE_X,
Y OUT DETAILS_ARRAY )
AS
l_rec DETAILS;
BEGIN
-- initialize output array
y := package_test.details_array();
FOR I IN 1..X.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(X(I));
SELECT EMPNO,ENAME,DEPTNO INTO lrec
FROM EMP
WHERE DEPTNO=X(I);
y.extend();
y(y.count) := lrec;
END LOOP;
END;
END;
This would be inefficient for a large number of rows. In that case you should look to use BULK COLLECT instead:
CREATE OR REPLACE PACKAGE BODY PACKAGE_TEST
AS
PROCEDURE PROC_TESTING_2(
X TYPE_X,
Y OUT DETAILS_ARRAY )
AS
l_rec DETAILS;
BEGIN
SELECT EMPNO,ENAME,DEPTNO
bulk collect INTO y
FROM EMP
WHERE DEPTNO in ( select * from table (X));
END;
END;

PL/SQL: ORA-00947: not enough values when creating function

I am creating a package with a function that returns a table. Here is the (simplified) code:
DROP TYPE A_TABLE;
/
CREATE OR REPLACE TYPE A_RECORD IS object (
ID varchar(10),
Name varchar(500)
);
/
CREATE TYPE A_TABLE IS TABLE OF A_RECORD;
/
CREATE OR REPLACE PACKAGE My_Package AS
results A_TABLE;
FUNCTION A_Data()
RETURN A_TABLE;
END My_Package;
/
CREATE OR REPLACE PACKAGE BODY My_Package IS
FUNCTION A_Data() RETURN A_TABLE IS results A_TABLE;
BEGIN
SELECT ID,Name
BULK COLLECT INTO results
FROM Customer_Table customer
RETURN results;
END A_Data;
END My_Package;
/
I get this error on compiling the package:
Error(9,13): PL/SQL: ORA-00947: not enough values
The query itself works although there is no data in there at the moment as the database is being worked on at the moment. I can't understand why this is breaking. I think it might be something to do with types?
You select two values into one record. It not work that way. You should create your record type in your select:
CREATE OR REPLACE PACKAGE BODY My_Package IS
FUNCTION A_Data() RETURN A_TABLE IS results A_TABLE;
BEGIN
SELECT A_RECORD(ID,Name)
BULK COLLECT INTO results
FROM Customer_Table customer
RETURN results;
END A_Data;
END My_Package;
/
The issue is
SELECT ID,Name -- here
BULK COLLECT INTO results
FROM Customer_Table customer
The objects of type A_RECORD need to be collected in result. so, call should be
SELECT A_RECORD(ID,Name) BULK ...`
Besides, I think you don't really need to create types externally. This should work:
create or replace PACKAGE My_Package AS
TYPE A_TABLE IS TABLE OF Customr_Table%rowtype;
FUNCTION A_Data
RETURN A_TABLE;
END My_Package;
/
CREATE OR REPLACE PACKAGE BODY My_Package
IS
FUNCTION A_Data
RETURN A_TABLE
IS
results A_TABLE;
BEGIN
SELECT ID, NAME BULK COLLECT INTO results FROM Customr_Table;
RETURN results;
END A_Data;
END My_Package;
/
And you can use it like this:
declare
v My_Package.A_Table;
begin
v := My_Package.A_data();
for i in 1 .. v.count loop
dbms_output.put_line(v(i).id || ' ' || v(i).Name);
end loop;
end;
/

ORA-00902 for function that returns a table [duplicate]

I am writing a function and i want to wrap it with table function so i can use with select query.
Here is my type declaration and the some lines of my functions
CREATE OR REPLACE PACKAGE TYPES
AS
TYPE CURSORTYPE IS REF CURSOR;
TYPE vbugsrec
IS
RECORD (
bug_id bugs.bug_id%TYPE,
facility bugs.facility%TYPE
);
TYPE vbugstable
IS
TABLE OF vbugsrec
INDEX BY BINARY_INTEGER;
END;
/
CREATE OR REPLACE PACKAGE BODY CustomQueries
AS
FUNCTION pendverifylist (myldapid IN userpass.ldapalias%TYPE,
maxrows IN PLS_INTEGER:= CustomQueries.maxrecords)
RETURN types.vbugstable
IS
datarows types.vbugstable;
var_useralias userpass.ldapalias%TYPE
:= UPPER (pendverifylist.myldapid) ;
CURSOR pendverify_cur (
cursor_var_alias IN userpass.ldapalias%TYPE,
cursor_var_mybugstatus IN bugs.bug_status%TYPE,
cursor_var_wild IN qa_list.component%TYPE
)
IS
SELECT buglist.bug_id, buglist.facility
FROM bugs buglist,
(SELECT qa.product, qa.component
FROM qa_list qa, userpass UP
WHERE qa.qa_id = UP.userid
AND UP.ldapalias = cursor_var_alias) plist
WHERE buglist.bug_status = cursor_var_mybugstatus
AND buglist.smr_state IN (SELECT fs.finalstate
FROM finalstates fs)
AND buglist.facility = plist.product
AND (buglist.product LIKE plist.component
OR plist.component = cursor_var_wild);
BEGIN
OPEN pendverifylist.pendverify_cur (cursor_var_alias => pendverifylist.var_useralias,
cursor_var_mybugstatus => CustomQueries.default_bugstatus,
cursor_var_wild => CustomQueries.wildcard);
FETCH pendverifylist.pendverify_cur
BULK COLLECT INTO pendverifylist.datarows
LIMIT LEAST (GREATEST (0, pendverifylist.maxrows),
CustomQueries.MAXRECORDS);
CLOSE pendverifylist.pendverify_cur;
RETURN pendverifylist.datarows;
END pendverifylist;
END CustomQueries;
/
When i want to use TABLE function like below, i get error.ORA-00902: invalid datatype
SELECT * FROM TABLE(CUSTOMQUERIES.PENDVERIFYLIST ( 'product', 50 ));
Can anyone please help what i am doing wrong here?
Thanks in advance
You're trying to use package-level types in plain SQL, which isn't allowed. The types declared in the package are not visible to or valid outside PL/SQL (or even in plain SQL statements within PL/SQL). A cut-down version of what you're doing:
create or replace package types as
type my_rec_type is record (dummy dual.dummy%type);
type my_table_type is table of my_rec_type index by binary_integer;
end types;
/
create or replace package p42 as
function get_table return types.my_table_type;
end p42;
/
create or replace package body p42 as
function get_table return types.my_table_type is
my_table types.my_table_type;
begin
select * bulk collect into my_table from dual;
return my_table;
end get_table;
end p42;
/
select * from table(p42.get_table);
SQL Error: ORA-00902: invalid datatype
Even within the package, if you had a procedure that tried to use the table function it would error. If you added:
procedure test_proc is
begin
for r in (select * from table(get_table)) loop
null;
end loop;
end test_proc;
... the package body compilation would fail with ORA-22905: cannot access rows from a non-nested table item.
You need to declare the types at schema level, not in a package, so using the SQL create type command:
create type my_obj_type is object (dummy varchar2(1));
/
create type my_table_type is table of my_obj_type;
/
create or replace package p42 as
function get_table return my_table_type;
end p42;
/
create or replace package body p42 as
function get_table return my_table_type is
my_table my_table_type;
begin
select my_obj_type(dummy) bulk collect into my_table from dual;
return my_table;
end get_table;
end p42;
/
select * from table(p42.get_table);
DUMMY
-----
X
Actually tehere is not need to have types in schema level. All you need to do is to define function as PIPELINED.
-- DEFINITION IN PCKG HEADER
create or replace PACKAGE "AAA" IS
TYPE t_record IS RECORD (
aaa VARCHAR(20 CHAR),
bbb VARCHAR(50 CHAR),
ccc VARCHAR(10 CHAR)
);
TYPE t_collection is table of t_record;
FUNCTION get_records(p_in1 DATE, p_in2 DATE) RETURN t_collection PIPELINED;
END AAA;
-- PCKG BODY
create or replace PACKAGE BODY AAA AS
FUNCTION get_records(p_in1 DATE, p_in2 DATE) RETURN t_collection PIPELINED AS
CURSOR k1 is SELECT aaa,bbb,ccc FROM table;
BEGIN
FOR rec IN k1
LOOP
pipe row( (rec) );
END LOOP;
END get_records
END AAA;
-- CALLING FUNCTION OUTSIDE OF PCKG
select * from TABLE(AAA.get_records(par1, par2));
Thanks to Alex Poole. This is what i ended up with
CREATE OR REPLACE TYPE vbugsrec
IS
OBJECT (
bug_id NUMBER(9),
facility VARCHAR2(256)
);
CREATE OR REPLACE TYPE vbugstable
IS
TABLE OF vbugsrec;
/
CREATE OR REPLACE PACKAGE BODY CustomQueries
AS
FUNCTION pendverifylist (myldapid IN userpass.ldapalias%TYPE,
maxrows IN PLS_INTEGER:= CustomQueries.maxrecords)
RETURN vbugstable
IS
datarows vbugstable := vbugstable();
var_useralias userpass.ldapalias%TYPE:= UPPER (pendverifylist.myldapid) ;
TYPE temp_rec IS RECORD (
bug_id bugs.bug_id%TYPE,
facility bugs.facility%TYPE
);
TYPE temp_records
IS
TABLE OF temp_rec
INDEX BY BINARY_INTEGER;
temporary_records temp_records;
CURSOR pendverify_cur (
cursor_var_alias IN userpass.ldapalias%TYPE,
cursor_var_mybugstatus IN bugs.bug_status%TYPE,
cursor_var_wild IN qa_list.component%TYPE
)
IS
SELECT buglist.bug_id, buglist.facility
FROM bugs buglist,
(SELECT qa.product, qa.component
FROM qa_list qa, userpass UP
WHERE qa.qa_id = UP.userid
AND UP.ldapalias = cursor_var_alias) plist
WHERE buglist.bug_status = cursor_var_mybugstatus
AND buglist.smr_state IN (SELECT fs.finalstate
FROM finalstates fs)
AND buglist.facility = plist.product
AND (buglist.product LIKE plist.component
OR plist.component = cursor_var_wild);
BEGIN
OPEN pendverifylist.pendverify_cur (cursor_var_alias => pendverifylist.var_useralias,
cursor_var_mybugstatus => CustomQueries.default_bugstatus,
cursor_var_wild => CustomQueries.wildcard);
FETCH pendverifylist.pendverify_cur
BULK COLLECT INTO temporary_records
LIMIT LEAST (GREATEST (0, pendverifylist.maxrows),
CustomQueries.MAXRECORDS);
CLOSE pendverifylist.pendverify_cur;
IF temporary_records.COUNT <> 0
THEN
FOR rec_idx IN temporary_records.FIRST .. temporary_records.LAST
LOOP
datarows.EXTEND;
datarows (datarows.LAST) :=
vbugsrec (temporary_records (rec_idx).bug_id,
temporary_records (rec_idx).facility);
END LOOP;
END IF;
RETURN pendverifylist.datarows;
END pendverifylist;
END CustomQueries;
/

How to use session-global variables of type collection in oracle

I have a package which declares a collection of type table of some database table's %rowtype. It also declares a function to populate the package-level variable with some data. I can now print the data with dbms_output, seems fine.
But when I use the package-level variable in some sql I get the following error:
ORA-21700: object does not exist or is marked for delete
ORA-06512: at "TESTDB.SESSIONGLOBALS", line 17
ORA-06512: at line 5
Here is my code:
create some dummy data:
drop table "TESTDATA";
/
CREATE TABLE "TESTDATA"
( "ID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(20 BYTE),
"STATUS" VARCHAR2(20 BYTE)
);
/
insert into "TESTDATA" (id, name, status) values (1, 'Hans Wurst', 'J');
insert into "TESTDATA" (id, name, status) values (2, 'Hans-Werner', 'N');
insert into "TESTDATA" (id, name, status) values (3, 'Hildegard v. Bingen', 'J');
/
now create the package:
CREATE OR REPLACE
PACKAGE SESSIONGLOBALS AS
type t_testdata is table of testdata%rowtype;
v_data t_testdata := t_testdata();
function load_testdata return t_testdata;
END SESSIONGLOBALS;
and the package body:
CREATE OR REPLACE
PACKAGE BODY SESSIONGLOBALS AS
function load_testdata return t_testdata AS
v_sql varchar2(500);
BEGIN
if SESSIONGLOBALS.v_data.count = 0
then
v_sql := 'select * from testdata';
execute immediate v_sql
bulk collect into SESSIONGLOBALS.v_data;
dbms_output.put_line('data count:');
dbms_output.put_line(SESSIONGLOBALS.v_data.count);
end if; -- SESSIONGLOBALS.v_data.count = 0
-- ******************************
-- this line throws the error
insert into testdata select * from table(SESSIONGLOBALS.v_data);
-- ******************************
return SESSIONGLOBALS.v_data;
END load_testdata;
END SESSIONGLOBALS;
execute the sample:
DECLARE
v_Return SESSIONGLOBALS.T_TESTDATA;
BEGIN
v_Return := SESSIONGLOBALS.LOAD_TESTDATA();
dbms_output.put_line('data count (direct access):');
dbms_output.put_line(SESSIONGLOBALS.v_data.count);
dbms_output.put_line('data count (return value of function):');
dbms_output.put_line(v_Return.count);
END;
If the line marked above is commented out i get the expected result.
So can anyone tell me why the exception stated above occurs?
BTW: it is absolutely nessecary for me to execute the statement which populates the collection with data as dynamic sql because the tablename is not known at compiletime. (v_sql := 'select * from testdata';)
the solution is to use pipelined functions in the package
see: http://docs.oracle.com/cd/B19306_01/appdev.102/b14289/dcitblfns.htm#CHDJEGHC ( => section Pipelining Between PL/SQL Table Functions does the trick).
my package looks like this now (please take the table script from my question):
create or replace
PACKAGE SESSIONGLOBALS AS
v_force_refresh boolean;
function set_force_refresh return boolean;
type t_testdata is table of testdata%rowtype;
v_data t_testdata;
function load_testdata return t_testdata;
function get_testdata return t_testdata pipelined;
END SESSIONGLOBALS;
/
create or replace
PACKAGE BODY SESSIONGLOBALS AS
function set_force_refresh return boolean as
begin
SESSIONGLOBALS.v_force_refresh := true;
return true;
end set_force_refresh;
function load_testdata return t_testdata AS
v_sql varchar2(500);
v_i number(10);
BEGIN
if SESSIONGLOBALS.v_data is null then
SESSIONGLOBALS.v_data := SESSIONGLOBALS.t_testdata();
end if;
if SESSIONGLOBALS.v_force_refresh = true then
SESSIONGLOBALS.v_data.delete;
end if;
if SESSIONGLOBALS.v_data.count = 0
then
v_sql := 'select * from testdata';
execute immediate v_sql
bulk collect into SESSIONGLOBALS.v_data;
end if; -- SESSIONGLOBALS.v_data.count = 0
return SESSIONGLOBALS.v_data;
END load_testdata;
function get_testdata return t_testdata pipelined AS
v_local_data SESSIONGLOBALS.t_testdata := SESSIONGLOBALS.load_testdata();
begin
if v_local_data.count > 0 then
for i in v_local_data.first .. v_local_data.last
loop
pipe row(v_local_data(i));
end loop;
end if;
end get_testdata;
END SESSIONGLOBALS;
/
now i can do a select in sql like this:
select * from table(SESSIONGLOBALS.get_testdata());
and my data collection is only populated once.
nevertheless it is quite not comparable with a simple
select * from testdata;
from a performace point of view but i'll try out this concept for some more complicated use cases. the goal is to avoid doing some really huge select statements involving lots of tables distributed among several schemas (english plural for schema...?).
The syntax you use does not work:
insert into testdata select * from table(SESSIONGLOBALS.v_data); -- does not work
You have to use something like that:
forall i in 1..v_data.count
INSERT INTO testdata VALUES (SESSIONGLOBALS.v_data(i).id,
SESSIONGLOBALS.v_data(i).name,
SESSIONGLOBALS.v_data(i).status);
(which actually duplicates the rows in the table)
Package-level types cannot be used in SQL. Even if your SQL is called from within a package, it still can't see that package's types.
I'm not sure how you got that error message, when I compiled the package I got this error, which gives a good hint at the problem:
PLS-00642: local collection types not allowed in SQL statements
To fix this problem, create a type and a nested table of that type:
create or replace type t_testdata_rec is object
(
"ID" NUMBER,
"NAME" VARCHAR2(20 BYTE),
"STATUS" VARCHAR2(20 BYTE)
);
create or replace type t_testdata as table of t_testdata_rec;
/
The dynamic SQL to populate the package variable gets more complicated:
execute immediate
'select cast(collect(t_testdata_rec(id, name, status)) as t_testdata)
from testdata ' into SESSIONGLOBALS.v_data;
But now the insert will work as-is.

Resources