I need to pass a locally defined table type to a function which is a pipelined function returning another locally defined table type.
Here is sample data:
create table my_tab
(i NUMBER,
n VARCHAR2(30));
insert into my_tab values (1, 'Peter');
insert into my_tab values (2, 'Dakshesh');
insert into my_tab values (1, 'Maggie');
insert into my_tab values (3, 'Madhu');
commit;
My code is:
CREATE OR REPLACE PACKAGE my_pkg IS
TYPE t_col IS RECORD(
i NUMBER,
n VARCHAR2(30));
TYPE t_nested_table IS TABLE OF t_col;
TYPE t_number IS TABLE OF NUMBER;
FUNCTION iterate_table RETURN t_number PIPELINED;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED;
g_number t_number ;
g_nested_number t_nested_table ;
END my_pkg;
/
Body:
CREATE OR REPLACE PACKAGE BODY my_pkg IS
FUNCTION iterate_table RETURN t_number PIPELINED IS
BEGIN
IF ( (g_number IS NOT NULL) AND (g_number.EXISTS (1)))
THEN
FOR i IN 1 .. g_number.COUNT
LOOP
IF g_number (i) IS NOT NULL
THEN
PIPE ROW (g_number (i));
END IF;
END LOOP;
END IF;
RETURN;
EXCEPTION
WHEN OTHERS
THEN
RAISE;
END iterate_table;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED IS
l_row t_nested_table ;
CURSOR cur_test IS
select mt.i, mt.n
from my_tab mt, TABLE(iterate_table ) tab
where mt.i = tab.column_value;
BEGIN
OPEN cur_test;
FETCH cur_test BULK COLLECT into l_row;
CLOSE cur_test;
FOR i IN 1..l_row.COUNT
LOOP
PIPE ROW(l_row(i));
END LOOP;
RETURN;
END return_table;
END my_pkg;
/
Now this code compiles successfully, when I try to invoke it like a pipelined function, it gives error-
select * from table(my_pkg.return_table(my_pkg.t_number(1)));
Error-
ORA-00902: invalid datatype
00902. 00000 - "invalid datatype"
*Cause:
*Action:
Error at Line: 14 Column: 41
The two pre-requisites for this code are -
the collections should be all locally defined.
the function should be pipelined.
Help!!
I am not getting any error with table and its a single table so no foreign key constraints hold.
It is not possible in oracle 11. I don't know why oracle throws "invalid data type".
If you put this query into anonymous block you will receive PLS-00642: Local Collection Types Not Allowed in SQL Statement
If you don't want or you cannot create sql level collection. The solution is use predefined type. Good source of predefined collection is Oracle Data Cartridge. Replace all t_number with ODCINumberList.
predefined collections
In Oracle 10 and 11, it is not possible to use collections in the SQL scope that have been defined in a PL/SQL scope. In Oracle 12, what you are trying should work.
If you want to do this then declare your types in the SQL scope (not in the package).
CREATE OR REPLACE TYPE t_col IS OBJECT(
i NUMBER,
n VARCHAR2(30)
);
/
CREATE OR REPLACE TYPE t_nested_table IS TABLE OF t_col;
/
CREATE OR REPLACE TYPE t_number IS TABLE OF NUMBER;
/
CREATE OR REPLACE PACKAGE my_pkg IS
FUNCTION iterate_table RETURN t_number PIPELINED;
FUNCTION return_table(in_t_num t_number) RETURN t_nested_table PIPELINED;
g_number t_number ;
g_nested_number t_nested_table ;
END my_pkg;
/
Related
I want to create one function which will return two table type in output.
Function will be like below:
create or replace function function_name (id number) RETURN ... ... ..
as
type i_conID is table of number;
type i_cov_lineID is table of number;
v_conid i_conID;
v_cov_lineid i_cov_lineID;
cursor p_cursor_name (id number) is
SELECT /*+ parallel(64)*/ con_id,cov_lineid
FROM table_name where sava_id like '%'||id||'%';
begin
open p_cursor_name(id);
loop
fetch p_cursor_name bulk collect into v_conid, v_cov_lineid;
exit when p_cursor_name%notfound;
end loop;
return .. ..
end ;
Here i need output for both v_conid, v_cov_lineid.
I can't create procedure, as we need to call in select statement.
Here is a code example how you can create the object type and use it with your example:
Create Type:
CREATE TYPE test_type AS object
(conid sys.odcinumberlist
,cov_lineid sys.odcinumberlist)
/
Your function:
create or replace function function_name (id number) RETURN test_type
as
res test_type;
BEGIN
SELECT /*+ parallel(64)*/ con_id,cov_lineid
BULK COLLECT INTO res.conid, res.cov_lineid
FROM table_name where sava_id like '%'||id||'%';
RETURN res;
END;
/
I am trying to do build a sentence using a UDT, as follows:
CREATE OR REPLACE TYPE fv_group as object(
fv NUMBER,
group_number INTEGER
);
/
CREATE OR REPLACE TYPE fv_group_array IS VARRAY(100) OF fv_group;
CREATE OR REPLACE TYPE fv_grouping AS OBJECT (
fv_and_group fv_group_array,
MEMBER PROCEDURE insert_groupby(FV NUMBER),
MEMBER FUNCTION which_group(FV NUMBER) RETURN INTEGER
);
/
CREATE OR REPLACE TYPE BODY fv_grouping as
MEMBER PROCEDURE insert_groupby(FV NUMBER) IS
g fv_group;
BEGIN
IF fv < 15 THEN
g := fv_group(fv,1);
ELSE
g := fv_group(fv,2);
END IF;
fv_and_group.extend(1);
fv_and_group(fv_and_group.last) := g;
END;
MEMBER FUNCTION which_group(FV NUMBER) RETURN INTEGER IS
feature NUMBER;
BEGIN
FOR i IN 1..fv_and_group.count LOOP
feature := fv_and_group(i).fv;
IF fv_and_group(i).fv = fv THEN
RETURN fv_and_group(i).group_number;
END IF;
END LOOP;
RETURN 0;
END;
END;
/
The representation I need is:
DECLARE
obj fv_grouping;
BEGIN
SELECT :obj.which_group(gb.fv), count(*)
FROM (
SELECT :obj.insert_groupby(c.fv, 6, 3)
from cophir
) gb
GROUP BY :obj.which_group(gb.fv);
END;
/
The procedure insert_groupby inserts each value of the cophir table into a varray, which holds its values and the corresponding group.
After the varray is loaded with all values and their corresponding group, I want to group them. Is it possible, to do it in a query?
Thanks in advance!
grouping is a type. What is wrong?
GROUPING is an Oracle keyword. If you re-name your type to say FV_GROUPING you will solve the PLS-00488. Which will leave you free to address all the other syntax errors in your code:
The object type is not instantiated correctly in the query.
SELECT statements embedded in PL/SQL must select into a variable which matches the projection of the query.
Not sure what your code is trying to achieve, but this version of your code runs:
DECLARE
obj fv_grouping;
BEGIN
SELECT fv_grouping(cast(collect(fv_group(fv, 3)) as fv_group_array))
into obj
from cophir;
dbms_output.put_line(obj.which_group(2)) ;
END;
/
This uses COLLECT to gather the fv_group objects into an fv_group_array instance which can be used to instantiate fv_grouping and populate the obj variable.
I have a PL/SQL stored proc which needs to return a RefCursor type object as output parameter.
PROCEDURE usp_appnt_stts_driver_wraper2
( in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec1 OUT SYS_REFCURSOR)
At the end of the SP, I am able to return Hard Coded values to my front end by using a Select Statement.
OPEN out_response_rec1 FOR
SELECT 'data1', 'data2', 'data3', 'data 4' FROM DUAL;
This works fine. But I need to send the data which I am getting from an Array.
The Array is populated like this,
FOR g_index IN g_slsnetoutbndarr.FIRST..g_slsnetoutbndarr.LAST
LOOP
out_response_rec.EXTEND;
IF g_SlsnetOutbndArr(g_index).Rectypdesc IS NOT NULL THEN
out_response_rec(g_index).Rectypdesc := g_SlsnetOutbndArr(g_index).Rectypdesc ;
out_response_rec(g_index).Recdetltcode := g_SlsnetOutbndArr(g_index).Recdetltcode;
out_response_rec(g_index).RecDetlDesc := g_SlsnetOutbndArr(g_index).RecDetlDesc ;
END IF;
END LOOP;
So at the end of this code, the Array Object out_response_rec has all the values I need.
But How do I transfer these values in the RefCursor output parameter?
Update 1
I have tried to create a new data type in the Package specification.
TYPE SlsnetOutbndRec IS RECORD(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130));
TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
Finally I have tried to Cast the Array to table in my SP as
OPEN out_response_rec_result FOR
SELECT * FROM TABLE (Cast(out_response_rec AS SlsnetOutbndTabArr));
But this is throwing an Invalid Data Type error. The SP does not recognize the new data types I created.
So at the end of this code, the Array Object out_response_rec has all
the values I need.
But How do I transfer these values in the RefCursor output parameter?
As I could understand, you wanted to use a SYS_REFCUSOR to get the output of a Query plus the values that are in collection which is being populated by another Procedure. I have come up with a solution for your requirement see below inline explaination:
--Create a Object in Place of Record since we cannot use a PLSQL scope compnenet in SQL scope in Oracle 11g and below
CREATE OR REPLACE TYPE SlsnetOutbndRec IS OBJECT
(
Rectypdesc VARCHAR2 (30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2 (130)
);
--Create a table type of the Object
CREATE OR REPLACE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec;
/
--Procedure
CREATE OR REPLACE PROCEDURE combined_rslt (var OUT SYS_REFCURSOR)
AS
v_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
l_var SlsnetOutbndTabArr := SlsnetOutbndTabArr ();
BEGIN
--Populating the collection
v_var.EXTEND;
v_var (1) := SlsnetOutbndRec ('ABC', 1, 'A');
OPEN VAR FOR
SELECT 'CDE', 2, 'B' FROM DUAL
UNION ALL -- Combining the result of collection with the result of query
SELECT *
FROM TABLE (v_var) t;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (SQLERRM);
END;
Execution :
DECLARE
x SYS_REFCURSOR;
a VARCHAR2 (30);
b NUMBER;
c VARCHAR2 (130);
BEGIN
combined_rslt (var => x);
LOOP
FETCH x INTO a, b, c;
EXIT WHEN x%NOTFOUND;
DBMS_OUTPUT.put_line (a || ' ' || b || ' ' || c);
END LOOP;
END;
Results:
SQL> /
CDE 2 B
ABC 1 A
PL/SQL procedure successfully completed.
You need to create the types in the SQL scope (not the PL/SQL scope) if you want to use them in SQL statements (in versions prior to Oracle 12):
CREATE TYPE SlsnetOutbndRec IS OBJECT(
Rectypdesc VARCHAR2(30),
Recdetltcode NUMBER,
RecDetlDesc VARCHAR2(130)
)
/
CREATE TYPE SlsnetOutbndTabArr IS TABLE OF SlsnetOutbndRec
/
Then you can use it in your procedure (assuming your 3rd party data type is a collection or a VARRAY):
PROCEDURE usp_appnt_stts_driver_wraper2(
in_req_src_system_id IN NUMBER,
in_req_user_info IN VARCHAR2,
out_response_rec_result OUT SYS_REFCURSOR
)
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
BEGIN
FOR i IN 1 .. g_slsnetoutbndarr.COUNT LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
END LOOP;
OPEN out_response_rec_result FOR
SELECT *
FROM TABLE( out_response_rec );
END;
If the 3rd party data type is an associative array then:
...
IS
out_response_rec SlsnetOutbndTabArr := SlsnetOutbndTabArr();
g_slsnetoutbndarr ThirdPartyDataType := Get_From_3rd_party_Package();
i ThirdPartyIndexType := g_slsnetoutbndarr.FIRST;
BEGIN
WHILE i IS NOT NULL LOOP
IF g_SlsnetOutbndArr(i).Rectypdesc IS NOT NULL THEN
out_response_rec.EXTEND;
out_response_rec := SlsnetOutbndRec(
g_SlsnetOutbndArr(i).Rectypdesc,
g_SlsnetOutbndArr(i).Recdetltcode,
g_SlsnetOutbndArr(i).RecDetlDesc
);
END IF;
i := g_slsnetoutbndarr.NEXT(i);
END LOOP;
...
Say I have this Object type:
create or replace
TYPE "InvestorInfoObject" AS OBJECT
(
"InvestorAccount" Varchar2(15 Char),
"InvestorSeqNo" NUMBER(15,0),
"FirstName" VARCHAR2(50 CHAR) ,
);
I need a function to select some values from different tables and return an "InvestorInfoObject". something like this:
create or replace
FUNCTION "GetInvestorInfo"
(
Par_InvestorAccount "Pos"."InvestorAccount"%Type
)
Return "InvestorInfoObject" As
investorObj "InvestorInfoObject";
Begin
Select
"InvestorInfoObject"(InvAccounts."InvestorAccount",InvAccounts."InvestorSeqNo",Individuals."FirstName")
into investorObj
From "InvestorAccounts" InvAccounts
Inner Join "Individuals" Individuals
On InvAccounts."InvestorSeqNo"=Individuals."Seq"
Where "InvestorAccount"=Par_Investoraccount;
RETURN Investorobj;
END "GetInvestorInfo";
but this results in Error "Error(17,5): PL/SQL: SQL Statement ignored". what is the correct syntax to do this?
if you are asking about returning more then one value that can be done using collection this
can be used
declare
type student_mark is varray(10) of integer;
x student_mark;
function double_marks(x student_mark) return
student_mark is
y student_mark;
begin
y := x;
for i in 1..x.count loop
y(i) := 2*y(i);
end loop;
return y;
end;
begin
x := student_mark(156, 206, 360);
dbms_output.put_line(double_marks(x)(1));
dbms_output.put_line(double_marks(x)(2));
dbms_output.put_line(double_marks(x)(3));
end;
here you can use object also instead of varray . i will get back to you once i execute the
code provided by you
reference: How to make a wrapper to return something other than ref cursor
I've got many wrapped functions that are similar to the one below, only they return different columns of course. How can i put them all in one package. because what the examples below does is replace my wrapper package everytime.
I want to know how to remove the global declarations on top so that i can add many in the same package each with different return columns
create or replace package WrapperSample is
type TResultRow is record(
if_type codes.cd%type
,number_infected Integer);
type TResultRowList is table of TResultRow;
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined;
end WrapperSample;
/
create or replace package body WrapperSample is
function GetADedIcWarningsProv
(
p_hos_id in work_entity_data.hos_id%type
,p_date in date
) return TResultRowList
pipelined is
v_refcur eOdatatypes_package.eOrefcur;
currentRow TResultRow;
begin
v_refcur := YourSchema.getADedIcWarningsProv(p_hos_id, p_date);
loop
fetch v_refcur
INTO currentRow;
exit when v_refcur%NotFound;
pipe row(currentRow);
end loop;
close v_refcur;
return;
end;
end WrapperSample;
/
you would use one record definition and one table definition per different column set.
create or replace package WrapperSample is
type R_WarningsProv is record(/*...*/);
type T_WarningsProv is table of R_WarningsProv ;
function GetADedIcWarningsProv(/*...*/) return T_WarningsProv pipelined;
type R_OtherFunction is record(/*...*/);
type T_OtherFunction is table of R_OtherFunction ;
function OtherFunction(/*...*/) return T_OtherFunction pipelined;
/* Multiple functions can use the same types as long as
they share the same column definition */
function SomeOtherFunction(/*...*/) return T_OtherFunction pipelined;
end WrapperSample;