How can I fetch the data from the SYS_REFCURSOR from one stored proc and use it in another? - sql

I have a stored proc with the basic layout below that returns a sys_refcursor as a result set. (Technically it reurns four but for the sake of clarity I just say one). The result set is a selection from a temp table.
procedure aProcedure
( C1 in out sys_refcursor
) is
begin
--populate Temp_Table here with a stored proc call;
OPEN C1 FOR
SELECT Cols
FROM TEMP_TABLE;
I need to insert this result set from C1 into a permanent table using a different stored procedure. Is this Do-able or do I need to re-build the result set all over again?
I've been able to find information on working with cursors and result sets outside of oracle but not for working with them within itself.
I know it might make sense to just do the insert from the first stored proc but that's not really how I need it to happen. It's an optional requirement to save the result set permanently.
Thanks for any helpful info.

Assuming that the caller knows the structure of the cursor that aProcedure is opening, you can do something like this.
declare
l_rc sys_refcursor;
l_rec temp_table%rowtype;
begin
aProcedure( l_rc );
loop
fetch l_rc
into l_rec;
exit when l_rc%notfound;
dbms_output.put_line( l_rec.col1 );
end loop;
close l_rc;
end;
/
If you can't fetch into a record type, you can also fetch into a number of other scalar local variables (the number and type have to match the number and type of columns that aProcedure specifies in its SELECT list). In my case, I defined aProcedure to return two numeric columns
declare
l_rc sys_refcursor;
l_col1 number;
l_col2 number;
begin
aProcedure( l_rc );
loop
fetch l_rc
into l_col1, l_col2;
exit when l_rc%notfound;
dbms_output.put_line( l_col1 );
end loop;
close l_rc;
end;

Related

How to make a function that takes code like "SELECT * FROM SOME_TABLE" as an input and returns a table as an output?

I want to create a function that takes some code as an input (e.g. Select * FROM SOME_TABLE) and returns the result of a query as an output.
I want to use it in procedures in order to return tables as a result.
It should look like this:
BEGIN
--some procedure code
CREATE TABLE SOME_TABLE as Select * FROM ...;
Select * FROM table(my_function('Select * FROM SOME_TABLE'));
END;
Important tips:
The resulting table can have multiple columns, from 1 to +inft
The resulting table can have multiple rows, from 1 to +inft
So the size of a table can be both very small or very large.
The input query can have several where, having, partition, and other Oracle constructions.
I want to have a table as an output, not DBMS_OUTPUT.
I can't install any modules/applications, or use other languages hints. However, I can manually create types, functions, procedures.
I tried to search in the net but could not find a solution that meets all my expectations. The best link I've found was this:
https://sqljana.wordpress.com/2017/01/22/oracle-return-select-statement-results-like-sql-server-sps-using-pipelined-functions/
DBMS_SQL.RETURN_RESULT works if your "code" is a select query
DECLARE
l_cur SYS_REFCURSOR;
l_query VARCHAR2(4000) := 'select * from SOME_TABLE';
BEGIN
OPEN l_cur for l_query;
DBMS_SQL.RETURN_RESULT(l_cur);
END;
/
you can create a function that has a string as parameter and return a cursor.
select statement you should pass as a string. in a function you can open a Cursor.
declare
v_sql varchar2(100) := 'select 1,2,3,4,5 from dual';
cur_ref SYS_REFCURSOR;
function get_data(in_sql in varchar2) return SYS_REFCURSOR
as
cur_ret SYS_REFCURSOR;
begin
OPEN cur_ret FOR in_sql;
return cur_ret;
end;
begin
:cur_ref := get_data(v_sql);
end ;
if your select statement is longer than 32K than you maybe should use a clob instead of varchar2 for your Parameter type. But you have to try that

How to display table from returned cursor in Oracle?

I need to get a table of bank's name which their bsb is equal to value given to the function.
Here is my code:
CREATE OR REPLACE Function FF(BSB_NUMBER IN BANK.BSB#%TYPE) RETURN SYS_REFCURSOR
IS
MY_CURSOR SYS_REFCURSOR;
BEGIN
OPEN MY_CURSOR for
select * from bank where bank.bsb# = BSB_NUMBER;
return MY_CURSOR;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Error ! There is no such account');
END FF;
/
And I run in with this but doesn't print anything. Appreciate any idea:
SELECT FindBankStaff(012878) FROM BANK;
Don't return the cursor, return the appropriate value.
Finally I saw this answer and implement it to my issue:
CREATE OR REPLACE FUNCTION SOME_FUNC_RETURNING_A_CURSOR RETURN SYS_REFCURSOR IS
csrLocal SYS_REFCURSOR;
BEGIN
OPEN csrLocal FOR SELECT NAME, BSB# FROM BANK;
RETURN csrLocal;
END SOME_FUNC_RETURNING_A_CURSOR;
/
DECLARE
aCursor SYS_REFCURSOR;
someVariable VARCHAR2(40);
some2 number;
BEGIN
aCursor := SOME_FUNC_RETURNING_A_CURSOR;
WHILE TRUE LOOP
FETCH aCursor INTO someVariable,some2;
EXIT WHEN aCursor%NOTFOUND;
DBMS_OUTPUT.PUT(someVariable);
DBMS_OUTPUT.PUT(' ');
DBMS_OUTPUT.PUT_LINE(some2);
END LOOP;
COMMIT;
END;
/
In Toad there is an option to do this.
You just have to create a bind variable as Ref cursor and invoke the procedure or function. Once the process runs successfully, the Data Grid is automatically populated with the result set of the Object (In your case the records from ref cursor). I suppose even in Oracle Sql Developer the Feature has been incorporated.
If you are using SQL Plus* then the best way is to declare a variable as ref cursor. Execute the piece of block and print the output using print command. Hope it helps...

Looping over cursor returned from procedure with many tables

I'm working with a stored procedure that I didn't write, which is long and contains numerous columns and joins. The procedure returns a cursor, which the application server (.NET, incidentally) picks up and iterates through.
I'm trying to intercept the cursor using SQLPlus and PL/SQL but I'm having a hard time trying to figure out how to set up the script. Here's what I have so far:
DECLARE
cur sys_refcursor;
BEGIN
adv_schema.report_proc('CAL','01-JAN-2011','01-JAN-2012','Y',cur);
OPEN cur FOR --??????
LOOP
FETCH cur INTO column1, column2;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.Put_Line ('First Name: '||column1||' Last Name: '||column2);
END LOOP;
END;
/
What do I put in the OPEN statement? All the examples I've seen of how to do this are overly simplified examples where some table 't' is created right there in the PL/SQL block, then a cursor is opened with a query to that table, to loop over. What about when a procedure returns a cursor to a complex query with multiple tables?
Assuming that the report_proc procedure is returning the cursor (i.e. the fourth parameter is defined as an OUT SYS_REFCURSOR), there is no need to OPEN the cursor in your code. The procedure is already opening it. You just need to fetch from it.
As #Justin has already mentioned, there is no need to open the cursor returned by the report_proc procedure, you only need to fetch from that cursor. Ref cursors are weak cursors(basically cursors that return no type) and in order to fetch from a weakly typed cursor, you need to know what to fetch. When you know what type a cursor returns you can declare a local structure to fetch into, like so:
DECLARE
-- example of record
-- in your case you have to know exactly
-- how many column and of which datatype your ref cursor returns
type T_Record is record(
column_1 number,
column_2 number
);
l_record T_Record;
cur sys_refcursor;
BEGIN
adv_schema.report_proc('CAL','01-JAN-2011','01-JAN-2012','Y',cur);
LOOP
FETCH cur
INTO l_record;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.Put_Line ('First Name: '||l_record.column1
||' Last Name: '||l_record.column2);
END LOOP;
END;
Also, if you simply need to print the content of a ref cursor, you can do it in SQL*Plus as follows:
SQL> variable cur refcursor;
SQL> exec adv_schema.report_proc('CAL','01-JAN-2011','01-JAN-2012','Y', :cur);
And then use print command to print the refcursor cur:
SQL> print cur;
Do I need to fetch every column that the cursor returns, or can I fetch a subset, say the first three.
No, you fetch everything, you cannot be selective in what column to fetch. It's not impossible however, but it will involve using dbms_sql package, specifically dbms_sql.describe_columns procedure to get information about columns for a cursor.
Just for consideration, If you know that a specific column is definitely present in a cursor, you could use xmlsequence() function to fetch a specific column specifying its name in the extract() function:
SQL> declare
2 type T_List is table of varchar2(123);
3 l_names T_List;
4 l_ref_cur sys_refcursor;
5
6 begin
7 open l_ref_cur
8 for select first_name, last_name
9 from employees
10 where rownum <= 5;
11
12 SELECT t.extract('ROW/FIRST_NAME/text()').getstringval()
13 bulk collect into l_names
14 FROM table(xmlsequence(l_ref_cur)) t;
15
16 for indx in l_names.first..l_names.last
17 loop
18 dbms_output.put_line(l_names(indx));
19 end loop;
20 end;
21 /
Result:
Ellen
Sundar
Mozhe
David
Hermann
PL/SQL procedure successfully completed

How to return a cursor populated with NESTED RECORD type from Oracle procedure

I need to return a cursor from Oracle procedure, the cursor has to contain RECORD types. I know how to solve this problem when the RECORD is simple, but how can this be solved for NESTED RECORDS?
There is a working block of code for simple RECORD:
-- create package with a RECORD type
create or replace package pkg as
-- a record contains only one simple attribute
type t_rec is RECORD (
simple_attr number
);
end;
/
-- create a testing procedure
-- it returns a cursor populated with pkg.t_rec records
create or replace procedure test_it(ret OUT SYS_REFCURSOR) is
type cur_t is ref cursor return pkg.t_rec;
cur cur_t;
begin
-- this is critical; it is easy to populate simple RECORD type,
-- because select result is mapped transparently to the RECORD elements
open cur for select 1 from dual;
ret := cur; -- assign the cursor to the OUT parameter
end;
/
-- and now test it
-- it will print one number (1) to the output
declare
refcur SYS_REFCURSOR;
r pkg.t_rec;
begin
-- call a procedure to initialize cursor
test_it(refcur);
-- print out cursor elements
loop
fetch refcur into r;
exit when refcur%notfound;
dbms_output.put_line(r.simple_attr);
end loop;
close refcur;
end;
/
Can you show me, how it could be done when a RECORD t_rec contains NESTED RECORD?
Modify the example in the folowing way:
-- create package with a NESTED RECORD type
create or replace package pkg as
type t_rec_nested is RECORD (
nested_attr number
);
-- a record with NESTED RECORD
type t_rec is RECORD (
simple_attr number,
nested_rec t_rec_nested
);
end;
/
create or replace procedure test_it(ret OUT SYS_REFCURSOR) is
type cur_t is ref cursor return pkg.t_rec;
cur cur_t;
begin
-- how to populate a result?
open cur for ????
ret := cur;
end;
/
The question is how to modify test_it procedure to populate a cursor?
I spent many hours searching the solution, I will appreciate any help.
I don't think it's possible as you have it, as RECORDs are a PL/SQL data type. You can do an equivalent thing by making an OBJECT. If you scroll down to the bottom of this link (or search for "Updating a Row Using a Record Containing an Object: Example" on the page), you will see how that is handled.

Get all records from a Ref Cursor in a Package

I'd like to fetch each row when I call the following function:
CpuReporting.getgridusage(300)
This function was provided in a CpuReporting package. It has:
Function GetGridUsage(minutes_Input in Number) Return CurGridUsage;
Type CurGridUsage is Ref Cursor Return RecGridUsage;
Type RecGridUsage is Record (ConfigId Number,
Phase VarChar2(400),
Environment VarChar2(400),
SessionStartTime Date,
I've browsed the GetGridUsage function in PL/SQL Developer, it has two parameters: (Result) REF CURSOR and MINUSTES_INPUT IN NUMBER
I want to be able to fetch all the rows and since I haven't worked with Ref Cursor before, I'm really interested in knowing how will you going to write the PL/SQL.
Assuming CpuReporting is the package name, you'd want something like
DECLARE
l_cursor CpuReporting.CurGridUsage;
l_rec CpuReporting.RecGridUsage;
BEGIN
l_cursor := CpuReporting.getGridUsage( 300 );
LOOP
FETCH l_cursor INTO l_rec;
EXIT WHEN l_cursor%notfound;
-- Do something with l_rec. As an example, print the Environment
dbms_output.put_line( l_rec.Environment );
END LOOP;
CLOSE l_cursor;
END;
If you just want to see the output of the function in PL/SQL you can do this
var r refcursor;
exec :r := CpuReporting.getgridusage(300);
print r;