Using of cursors as function output in procedure (Oracle) - oracle

I'm trying to find a solution but all the time something is wrong. So what is my problem:
I have a function:
function fun1 (
p_param1 number) return sys_refcursor
is
c_result sys_refcursor;
begin
open c_result for select e.username, c.contract_id from employees e
join contracts c on c.employee_id = e.employee_id;
return c_result;
end fun1;
I want to use this function inside my stored procedure:
procedure proc1 (...)
is ...
cur_contract sys_refcursor;
begin
...
open cur_contract for fun1(p_param1);
loop
fetch cur_contract into v_username, v_contract_id;
exit when cur_contract%notfound;
...
end loop;
close cur_contract;
...
end proc1;
And I get error: expression is of wrong type in line "open cur_contract for fun1(p_param1);"
What should I change to make my procedures work?

You've already opened the cursor in fun1. Try the following:
procedure proc1 (...)
is
...
cur_contract sys_refcursor;
begin
...
cur_contract := fun1(p_param1);
loop
fetch cur_contract into v_username, v_contract_id;
exit when cur_contract%notfound;
...
end loop;
close cur_contract;
...
end proc1;
I hope this helps.

Related

PL/SQL Function - Bulk Collect into and Pipe Row

I am new to PL/SQL have issue with output value of a function.
I want to execute a SQL statement in a function and return the results. The function will be executed with following command: select * from table(mypkg.execute_query('1'));
I was using following article as refence "Bulk Collect Into" and "Execute Immediate" in Oracle, but without success.
It seems that I am using wrong data type. System returns issue on following line: PIPE Row(results)
create or replace package mypkg
as
type node is table of edges%ROWTYPE;
function execute_query (startNode in varchar2) RETURN node PIPELINED;
end;
create or replace package body mypkg
as
function execute_query(startNode in varchar2) RETURN node PIPELINED
AS
results node;
my_query VARCHAR2(100);
output VARCHAR2(1000);
c sys_refcursor;
BEGIN
my_query := 'SELECT DISTINCT * FROM EDGES WHERE src='|| startNode;
open c for my_query;
loop
fetch c bulk collect into results limit 100;
exit when c%notfound;
PIPE Row(results);
end loop;
close c;
END;
end;
I tried several options with cursor but wasn't able to return the value. If you have idea how to return the data by using something else than PIPELINED, please let me know.
Thanks for your support!
Fixed body:
create or replace package body mypkg
as
function execute_query(startNode in varchar2) RETURN node PIPELINED
AS
results node;
my_query VARCHAR2(100);
output VARCHAR2(1000);
c sys_refcursor;
BEGIN -- don't use concatenation, it leads to sql injections:
my_query := 'SELECT DISTINCT * FROM EDGES WHERE src=:startNode';
-- use bind variables and bind them using cluase "using":
open c for my_query using startNode;
loop
fetch c bulk collect into results limit 100;
-- "results" is a collection, so you need to iterate it to pipe rows:
for i in 1..results.count loop
PIPE Row(results(i));
end loop;
exit when c%notfound;
end loop;
close c;
END;
end;
/

return data as cursor from function in pl/sql wiithout create type oracle 11g

I wrote this code in pl/sql but I couldnt take answer.
create or replace function mostafa.sbs_Topic_LedgerBalance8Column
(BranchID number,DateFrom number,DateTo number)
RETURN SYS_REFCURSOR
IS O_RESULT SYS_REFCURSOR;
BEGIN
open O_RESULT for
Select s* From Mostafa.topic ;
RETURN O_RESULT;
end sbs_Topic_LedgerBalance8Column;
and I called it this way:
DECLARE v_refcursor SYS_REFCURSOR;
BEGIN
v_refcursor :=mostafa.sbs_topic_ledgerbalance8column(12,12,12);
FOR employee_rec IN v_refcursor
LOOP
DBMS_OUTPUT.put_line (
employee_rec.ID);
END LOOP;
end;
why did I get error when I retrieve result?
error is :v_refcursor is not a procedure or is undefined
When you are using a refcursor, you can't access it by using the cursor for loop. Use something like the following instead (Untested):
DECLARE
v_refcursor SYS_REFCURSOR;
v_emp_rec topic%ROWTYPE;
BEGIN
v_refcursor :=mostafa.sbs_topic_ledgerbalance8column(12,12,12);
LOOP
FETCH v_refcursor INTO v_emp_rec;
EXIT WHEN v_refcursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp_rec.id);
END LOOP;
close v_refcursor;
END;

Pass cursor name from a variable

I have a package that contains a procedure(takes an input parameter;cursor name) and two cursors.
The procedure is to use the cursor specified by the input parameter
PROCEDURE insertdetails(typ IN VARCHAR2) IS
BEGIN
OPEN typ;
LOOP
FETCH typ INTO colA;
EXIT WHEN typ%notfound;
--MISSING CODE
END LOOP;
END;
if i run the procedure and pass on of the name of the cusrsor as an input parameter i get the error PLS-00456: item 'TYP' is not a cursor
Is their a way out of this
You will need to use REF CURSORS to do this. Here's a pretty good explanation.
Your procedure definition would look like this:
PROCEDURE insertdetails(typ IN sys_refcursor) IS
You would probably not open the cursor in this procedure, generally you open it elsewhere, in the code where the cursor is defined.
Assuming the cursors are declared elsewhere in the package, you could use the parameter to decide which actual cursor to work with, something like:
PROCEDURE insertdetails(typ IN VARCHAR2) IS
BEGIN
IF typ = 'CURSOR_A' THEN
OPEN cursor_a;
LOOP
FETCH cursor_a INTO colA;
EXIT WHEN cursor_a%notfound;
--MISSING CODE
END LOOP;
ELSE
OPEN cursor_b;
LOOP
FETCH cursor_b INTO colA;
EXIT WHEN cursor_b%notfound;
--MISSING CODE
END LOOP;
END IF;
END;
Or if the missing code is common, as seems likely:
PROCEDURE insertdetails(typ IN VARCHAR2) IS
BEGIN
IF typ = 'CURSOR_A' THEN
OPEN cursor_a;
ELSE
OPEN cursor_b;
END IF;
LOOP
IF typ = 'CURSOR_A' THEN
FETCH cursor_a INTO colA;
EXIT WHEN cursor_a%notfound;
ELSE
FETCH cursor_b INTO colA;
EXIT WHEN cursor_b%notfound;
END IF;
--MISSING CODE
END LOOP;
END;
Either way you might prefer a case over an if; particularly if the number of possible cursors that typ can represent grows.

Failing to create and execute PL/SQL procedure from SQL*Plus

create or replace package XXX_MWA_LOV_TEST AS
TYPE t_ref_csr IS REF CURSOR;
PROCEDURE XXX_USERS_LOV (p_user_name IN VARCHAR2,x_users OUT NOCOPY t_ref_csr);
end XXX_MWA_LOV_TEST;
How to run XXX_USERS_LOV procedure in SQL*Plus?
I tried this code but getting the syntax error:
SQL> EXECUTE XXX_MWA_LOV_TEST.XXX_USERS_LOV('%',:my_p_out);
Procedure body:
create or replace package body XXX_MWA_LOV_TEST AS
PROCEDURE XXX_USERS_LOV (x_users OUT NOCOPY t_ref_csr,p_user_name IN VARCHAR2) IS
BEGIN
OPEN x_users FOR select user_id,user_name,description
from fnd_user
where user_name like p_user_name;
DBMS_OUTPUT.PUT_LINE('TESTING DATA');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERROR IN USER LOV '|| SQLERRM);
END XXX_USERS_LOV;
end XXX_MWA_LOV_TEST;
First of all, the order of the parameters must be the same in the spec and in the package body.
You can call the ref cursor like this:
declare
v_cursor xxx_mwa_lov_test.t_ref_csr;
var1 the_type_you_need;
var2 the_type_you_need;
...
begin
xxx_mwa_lov_test.xxx_users_lov(v_cursor,'username...');
loop
fetch v_cursor into var1, var2...;
exit when v_cursor%notfound;
... do what you want with the vars...
end loop;
end;
/

Inserting a RefCursor into a table

I have a simple function where I run a stored procedure that returns a RefCursor and I try to use that RefCursor to insert data to a temporary table. I get the following error when trying to do so:
SQL Error: ORA-00947: not enough values
I know for a fact that the refcursor returns exactly the same number of values as the temporary table has, correct column names, their order and their type. I ran print RefCursor and I can see all of the data. Here's the code:
var r refcursor;
EXEC SCHEMA.PACKAGE.SPROC(:r);
insert into SCHEMA.TEMP_TABLE
values
(r);
I have to add that the stored procedure has a refcursor defined as a OUT parameter so it returns a correct type. Using print r; prints the correct data.
What am I doing wrong?
EDIT:
Based on a suggestion I tried to use a fetch to a rowtype variable, but getting Invalid Number exception whenever I attempt to fetch a row:
DECLARE
cur SYS_refcursor;
rec SCHEMA.TEMP_TABLE%rowtype;
begin
SCHEMA.PACKAGE.SPROC( cur );
LOOP
FETCH cur INTO rec;
EXIT WHEN cur%NOTFOUND;
INSERT INTO SCHEMA.TEMP_TABLE
VALUES rec;
END LOOP;
EXCEPTION
WHEN INVALID_NUMBER THEN
DBMS_output.put_line(rec.move_id);
end;
I added the exception block to see which row is failing and needless to say it is the first one. The stored procedure I run returns a refcursor of a select query from multiple tables. The temporary table defined as the exact copy of the refcursor columns and their types. Not sure what could be causing the exception.
You can't insert into a table from a refcursor. You could write a procedure that fetches from the cursor and inserts into the table. If schema.package.sproc returns a ref cursor of temp_table%rowtype, you could do something like
DECLARE
cur sys_refcursor;
rec schema.temp_table%rowtype;
BEGIN
schema.package.sproc( cur );
LOOP
FETCH cur INTO rec;
EXIT WHEN cur%NOTFOUND;
INSERT INTO schema.temp_table
VALUES rec;
END LOOP;
END;
You can use LOOP + FETCH to filter the row in SYS_REFCURSOR.
Exit the LOOP using "EXIT WHEN ref_%notfound;"
Example: Have 2 functions.
FUNCTION get_data
RETURN SYS_REFCURSOR
is
s varchar2(2000);
ref_ SYS_REFCURSOR;
begin
s := 'select username, password, email from user_info where id < 100';
OPEN ref_ FOR s;
return ref_;
end;
FUNCTION load_data_in_table
RETURN varchar2
is
s varchar2(2000);
puser_name varchar2(2000);
ppassword varchar2(2000);
pemail varchar2(2000);
ref_ SYS_REFCURSOR;
begin
ref_ := get_data();
LOOP
--process_record_statements;
FETCH ref_ into puser_name, ppassword, pemail;
s := 'INSERT INTO TEST_USER_EXP VALUES(:user_name, :password, :email)';
EXECUTE IMMEDIATE s USING puser_name, ppassword, pemail;
EXIT WHEN ref_%notfound;
END LOOP;
commit;
return 'OK';
end;

Resources