inconsistent datatypes: when returning table from cursor in a package function - ORACLE - sql

I have the following package. I am trying to fill the records inside the function from a cursor and return the record. I am unsure of how to assign the rows from the cursor into the record variable. I need to return the record so that I could create a materialized view from it.
CREATE OR REPLACE PACKAGE pkg_contrator_of_consultant AS
TYPE cst_record IS RECORD(
consultant_id NUMBER(10));
TYPE cst_id_type IS TABLE OF cst_record;
FUNCTION fnc_get_contractor_id(cst_username IN VARCHAR2)
RETURN cst_id_type
PIPELINED;
END;
CREATE OR REPLACE PACKAGE BODY pkg_contrator_of_consultant AS
FUNCTION fnc_get_contractor_id(cst_username IN VARCHAR2 )
RETURN cst_id_type
PIPELINED IS
V_cursor_contracotr_id cst_id_type;
CURSOR cursor_contract_name IS
SELECT plc.FK2_CONTRACTOR_ID
FROM lds_consultant cons
INNER JOIN lds_account acc on cons.consultant_id = acc.fk1_consultant_id
INNER JOIN lds_placement plc on acc.account_id = plc.FK1_ACCOUNT_ID
WHERE UPPER(cons.USERNAME) = UPPER(cst_username)
AND UPPER(plc.PLT_TO_PERMANENT) = UPPER('Y');
V_contracotr_id cst_id_type;
BEGIN
FOR rec IN cursor_contract_name LOOP
V_contracotr_id := rec.fk2_contractor_id;
SELECT V_contracotr_id INTO V_cursor_contracotr_id FROM DUAL;
dbms_output.put_line('cst_username : '||cst_username||' V_contracotr_id :'||V_contracotr_id);
END LOOP;
PIPE ROW (V_cursor_contracotr_id);
RETURN;
END fnc_get_contractor_id;
END;
/
In the line
V_contracotr_id := rec.fk2_contractor_id;
It gives the error "inconsistent datatypes: expected UDT got NUMBER" when the column selected by the cursor is of NUMBER type.
FK2_CONTRACTOR_ID NUMBER

Try this:
out_rec cst_record;
CURSOR C1 IS
SELECT ...;
BEGIN
open c1;
LOOP
FETCH c1 INTO out_rec;
exit when c1%notfound;
PIPE ROW(out_rec);
END LOOP;
close c1;
RETURN;
END fnc_get_contractor_id;
UPDATED CODE:
CREATE OR REPLACE PACKAGE pkg_contrator_of_consultant AS
TYPE cst_record IS RECORD(
consultant_id NUMBER(10));
TYPE cst_id_type IS TABLE OF cst_record;
FUNCTION fnc_get_contractor_id(cst_username IN VARCHAR2)
RETURN cst_id_type
PIPELINED;
END;
/
CREATE OR REPLACE PACKAGE BODY pkg_contrator_of_consultant AS
FUNCTION fnc_get_contractor_id(cst_username IN VARCHAR2 )
RETURN cst_id_type
PIPELINED IS
CURSOR c1 IS
SELECT plc.FK2_CONTRACTOR_ID
FROM lds_consultant cons
INNER JOIN lds_account acc on cons.consultant_id = acc.fk1_consultant_id
INNER JOIN lds_placement plc on acc.account_id = plc.FK1_ACCOUNT_ID
WHERE UPPER(cons.USERNAME) = UPPER(cst_username)
AND UPPER(plc.PLT_TO_PERMANENT) = UPPER('Y');
out_rec cst_record;
BEGIN
open c1;
LOOP
FETCH c1 INTO out_rec;
exit when c1%notfound;
PIPE ROW(out_rec);
END LOOP;
close c1;
RETURN;
END fnc_get_contractor_id;
END;
/

Related

How can I return a SELECT query with an oracle function?

I need to create a function that allows me to return the same result as a SELECT query and that contains pl/sql code.
I tried something really simple :
create or replace FUNCTION test
RETURN SYS_REFCURSOR
IS
l_rc SYS_REFCURSOR;
BEGIN
OPEN l_rc
FOR SELECT *
FROM my_table;
RETURN l_rc;
END;
But when I call my function with SELECT test from dual;, I get all result from my_table in a single cell instead of having each columns separated.
Is there a way of doing what I want ?
Ideally, I want a view but there seems to be no way of adding logical conditions with them.
the function has to be pipelined. For example :
TYPE MyType IS RECORD(ID NUMBER);
TYPE MyTableType IS TABLE OF MyType;
Function MyFunction(Arguments) return MyTableType pipelined is
Cursor Cur is select * from whetever;
R Cur%rowtype;
Begin
Open cur;
loop
fetch Cur into R;
exit when Cur%notfound;
pipe row(R);
End loop;
Close cur;
End MyFunction;
Then you can call it via :
select * from table(MyFunction(Arguments));
The simplest way is to leave the function as it is and only call it properly:
create table my_table (id, memo) as
select 1, 'some memo' from dual
/
create or replace function MyTableById (id int) return sys_refcursor is
rc sys_refcursor;
begin
open rc for
select * from my_table where id=MyTableById.id;
return rc;
end;
/
var rc refcursor
exec :rc := MyTableById (1);
print rc
ID MEMO
---------- ---------
1 some memo

Invalid Data Type error while casting PL/SQL Array to Table

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;
...

How to store result of a function which will return sysrefcursor?

Scenario: there is an procedure inside which we have a cursor.
I need to call a function which will take an input from that cursor value and will return SYS_REFCURSOR.
I need to store that result of function in a different variable/cursor & need to return this value from procedure as out parameter.
I am using Oracle 11g.
How can I proceed?
PFB My Approach:
create or replace procedure prc_test
(p_dept_id in number,
c_detail out sysrefcursor)--need to add extra out parameter
as
var1 varchar2(200) :=null;
begin
open c_detail for
select -1 from dual;
if p_dept_id is not null then
open c_detail for
select emp_no from emp
where dept_id=p_dept_id;
--i need to retrn value of 'get_emp_dtls' function as out parameter.
end if;
end procedure;
/
Function to be called
CREATE OR REPLACE FUNCTION get_emp_dtls
(p_emp_no IN EMP.EMP_NO%TYPE)
RETURN SYS_REFCURSOR
AS
o_cursor SYS_REFCURSOR;
BEGIN
OPEN o_cursor FOR
SELECT
ENAME,
JOB
FROM emp
WHERE EMP_NO = p_emp_no;
RETURN o_cursor;
-- exception part
END;
/
Here is your function that takes a varchar2 variable and returns A refcursor( weakly typed).
CREATE OR replace FUNCTION fn_return_cur(v IN VARCHAR2)
RETURN SYS_REFCURSOR
IS
c1 SYS_REFCURSOR;
BEGIN
OPEN c1 FOR
SELECT 'ABC'
FROM dual
WHERE 'col1' = v;
RETURN c1;
END;
/
Here is the procedure that has a cursor value passed as argument to function and the returned cursor passed as OUT argument.
CREATE OR replace PROCEDURE Pr_pass_out_cur(v_2 OUT SYS_REFCURSOR)
IS
func_arg VARCHAR2(3);
other_arg VARCHAR2(3);
CURSOR c_2 IS
SELECT 'ABC' col1,
'DEF' col2
FROM dual;
BEGIN
LOOP
FETCH c_2 INTO func_arg, other_arg;
EXIT WHEN c_2%NOTFOUND;
v_2 := Fn_return_cur(func_arg);
END LOOP;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
/
Let me know your feedback.

PLS-00306 on Oracle function using a UDT

I am getting this error:
LINE/COL ERROR
-------- -----------------------------------------------------------------
13/3 PL/SQL: Statement ignored
13/13 PLS-00306: wrong number or types of arguments in call to 'JOIN_JT'
Types used:
CREATE TYPE join_t IS OBJECT (
inn NUMBER(38),
out NUMBER(38)
);
/
CREATE TYPE join_jt IS TABLE OF join_t;
/
Here is the PL/SQL code from the function that is returning the error. When I try to pass the results I have got in join_table to retval the type error above is triggered):
CREATE OR REPLACE FUNCTION join RETURN join_jt
AS
CURSOR cur_fv_table IS SELECT id,fv FROM london WHERE id <= 3000;
retval join_jt := join_jt ();
var_fv london.fv%type;
var_id london.id%type;
join_table join_jt := join_jt();
BEGIN
OPEN cur_fv_table;
LOOP
FETCH cur_fv_table INTO var_id,var_fv;
SELECT join_t(r.id, var_id) BULK COLLECT INTO join_table
FROM london r
WHERE manh_dist(r.fv,var_fv) <= 5;
retval.EXTEND;
retval := join_t(join_table);
END LOOP;
RETURN join_table;
END;
/
You can use this function for testing the function above:
CREATE OR REPLACE FUNCTION manh_dist(
fv1 LONDON.FV%TYPE,
fv2 LONDON.FV%TYPE
) RETURN NUMBER
AS
BEGIN
RETURN 0; -- Implement this.
END;
/
Does anyone know how to solve this error?
I am using the Oracle 11g.
So this is your problem:
retval := join_t (join_table);
You're trying to cast a table to an object type. Which is wrong. To populate the output table you need to union the query collection with the return collection. MULTISET UNION is what you need:
CREATE OR REPLACE FUNCTION knn_join RETURN join_jt
IS
CURSOR cur_fv_table IS SELECT id,fv FROM londonfv WHERE id <= 3000;
retval join_jt := join_jt ();
var_fv londonfv.fv%type;
var_id londonfv.id%type;
join_table join_jt := join_jt();
BEGIN
OPEN cur_fv_table;
LOOP
FETCH cur_fv_table INTO var_id,var_fv;
SELECT join_t(r.id, var_id) BULK COLLECT
INTO join_table FROM londonfv r WHERE manhattan_dist(r.fv,var_fv) <=5;
retval := retval multiset union all join_table;
END LOOP;
RETURN retval;
END;
/
Note: I assume you really meant to return the aggregated collection retval rather than the last intermediate set.
Not having time to test this right now, I admit #Wernfried has given me some doubt as to whether this will run. If you run into problems, this blunter approach will work:
for idx in join_table.first()..join_table.last()
loop
Retval.extend();
retval(retval.count()) := join_table(idx);
end loop;
The mistake you are making is while storing the result. See my comments inline
retval := join_t (join_table);
CREATE OR REPLACE FUNCTION knn_join
RETURN join_jt
IS
CURSOR cur_fv_table
IS
SELECT id, fv
FROM londonfv
WHERE id <= 3000;
retval join_jt := join_jt ();
var_fv londonfv.fv%TYPE;
var_id londonfv.id%TYPE;
join_table join_jt := join_jt ();
BEGIN
OPEN cur_fv_table;
LOOP
--Fetching records of cursor to variable var_id & var_fv
FETCH cur_fv_table INTO var_id, var_fv;
SELECT join_t (r.id, r.fv) -- You made mistake here. You need to select your table columns here not any variable.
BULK COLLECT INTO join_table --- Populating the collection
FROM londonfv r
WHERE manhattan_dist (var_id, var_fv) <= 5; -- Checking from the function
--- Assuming there is only 1 record in collection join_table.
retval.EXTEND;
--- Storing the value of into the collection
retval := join_table;
/* If there are more then
for rec in 1..join_table.count
loop
retval.EXTEND;
retval(rec):= join_table(rec);
end loop;
*/
END LOOP;
RETURN retval;
END;
/

PLS-00103: Encountered the symbol "IS" when expecting one of the following

I am trying to create a function right now (instead of a package) that selects the column data that is currently inside of my OP_GUIDE_VIEW.
Just need a function that selects what's there. Not modifying anything, don't think I need parameters or anything either. What I have so far is the following; however, I cannot figure out this error to save my life: - Has an error # IS C1 V_CURSOR;
What I have right now:
create or replace
PACKAGE PKG_OPGUIDE_COLLECTIONDATA AS
type v_cursor is ref cursor
return OPGUIDE_VIEW%rowtype;
c2 v_cursor;
r_c2 c2%rowtype;
function fn_opview return v_cursor
is c1 v_cursor;
begin
open c1 for select * from OPGUIDE_VIEW;
return c1;
end;
begin
c2 := fn_opview;
loop
fetch c2 into r_c2;
exit when c2%notfound;
dbms_output.put_line(initcap(r_c2.JOB_NAME));
end loop;
close c2;
end;
END PKG_OPGUIDE_COLLECTIONDATA;
Error:
PLS-00103: Encountered the symbol "IS" when expecting one of the following:
constant exception <an identifier>
<a double-quoted delimited-identifier> table long double ref
char time timestamp interval date binary national character
nchar
Can give more information if needed.
You're trying to create a package header and body at the same time.
You have to create an header, then a body, with separate queries; here you find something more.
An example of how you could edit your code:
setup:
create table OPGUIDE_VIEW(JOB_NAME varchar2(100));
insert into OPGUIDE_VIEW values ('job1');
insert into OPGUIDE_VIEW values ('job2');
package creation:
CREATE OR REPLACE PACKAGE PKG_OPGUIDE_COLLECTIONDATA AS
FUNCTION fn_opview
RETURN SYS_REFCURSOR;
PROCEDURE printData;
END;
/
CREATE OR REPLACE PACKAGE BODY PKG_OPGUIDE_COLLECTIONDATA AS
FUNCTION fn_opview
RETURN SYS_REFCURSOR IS
c1 SYS_REFCURSOR;
BEGIN
OPEN c1 FOR SELECT * FROM OPGUIDE_VIEW;
RETURN c1;
END;
PROCEDURE printData IS
c2 SYS_REFCURSOR;
r_c2 OPGUIDE_VIEW%ROWTYPE;
BEGIN
c2 := fn_opview();
LOOP
FETCH c2 INTO r_c2;
EXIT WHEN c2%NOTFOUND;
DBMS_OUTPUT.put_line(INITCAP(r_c2.JOB_NAME));
END LOOP;
CLOSE c2;
END;
END PKG_OPGUIDE_COLLECTIONDATA;
Call your procedure:
SQL> set serveroutput on
SQL>
SQL> begin
2 PKG_OPGUIDE_COLLECTIONDATA.printData;
3 end;
4 /
Job1
Job2
PL/SQL procedure successfully completed.
SQL>