how to get value of column from sys_refcursor returned by a function being called - sql

I am calling a function pck_my_pack.f_my_func in my anonymous block , f_my_func returns a sys_refcursor.
I want the value of req_column from sys_refcursor to used as
IF req_column>0 THEN
do this and that
END IF;
Problem is sys_refcursor which is being returned by my_func does not select values from a specific table. it selects values from multiple tables and few locals variables which were calculated in my_func.
Please help me out to get the value from this sys_refcursor.
Here is what I am trying:
DECLARE
TYPE cu_income_detail is ref cursor;
income_det cu_income_detail;
BEGIN
income_det := pck_my_pack.f_my_func(2542586);
FOR CURRVAL IN income_det LOOP -- I have also tried CUR_VAL
IF CURRVAL.pcls_paid_as_income > 0 THEN
-- Logic yet to be implemented once get the value
dbms_output.put('PCLS paid');
dbms_output.put_line(CUR_VAL.pcls_paid_as_income);
END IF;
END LOOP;
END;

declare
src_cur sys_refcursor;
curid NUMBER;
v_jobtitle varchar2(35);
v_min_salary number;
begin
open src_cur for 'select job_id,job_title,min_salary,max_salary from jobs';
curid := DBMS_SQL.TO_CURSOR_NUMBER(src_cur);
dbms_sql.define_column(curid,2,v_jobtitle,35);
dbms_sql.define_column(curid,3,v_min_salary);
WHILE DBMS_SQL.FETCH_ROWS(curid) > 0 LOOP
DBMS_SQL.COLUMN_VALUE(curid, 2, v_jobtitle);
DBMS_SQL.COLUMN_VALUE(curid, 3, v_min_salary);
DBMS_OUTPUT.put_line('job_titile='||v_jobtitle||' ;min_salary='||v_min_salary);
END LOOP;
DBMS_SQL.CLOSE_CURSOR(curid);
end;

You have to know type and name of columns in your sys_refcursor.
And perform fetch into operation on it.
DECLARE
TYPE cu_income_detail is ref cursor;
income_det cu_income_detail;
BEGIN
income_det := pck_my_pack.f_my_func(2542586);
loop
fetch income_det into var_1, var_2 ... var_3;
-- Logic here
exit when income_det%notfound;
end loop;
END;

Related

how to check if SYS_REFCURSOR is empty

I have a general SYS_REFCURSOR that I want to check if it is empty or not.
The code is like this:
declare
v_cursor SYS_REFCURSOR;
begin
OPEN v_cursor FOR <any select statement>.
check if v_cursor is empty.
end;
Can someone tell me how to check if the weak cursor is empty, please?
I have to mention that the base SELECT statement can be anything from any table.
The column numbers or type it is known only at runtime.
Thank you,
You can't see if a ref cursor contains data without fetching, and consuming at least one row from it.
If you really need to determine this at the point the cursor is opened, without knowing the structure at that point, you could execute a modified query that just counts the rows returned by your real query - possibly limited to a single row if that helps performance - either as a simple execute immediate ... into ... or with a separate open/fetch/close for consistency; something like:
declare
v_cursor SYS_REFCURSOR;
v_query VARCHAR2(4000);
v_count PLS_INTEGER;
begin
v_query := <any select statement>;
-- see if the query finds any data
OPEN v_cursor FOR 'select count(*) from (' || v_query || ')'; -- could limit rows
FETCH v_cursor INTO v_count;
CLOSE v_cursor;
if v_count = 0 then
dbms_output.put_line('No data');
return;
end if;
dbms_output.put_line('Found data, opening cursor for real');
OPEN v_cursor FOR v_query;
-- loop over results, return to caller, etc.
end;
/
db<>fiddle
Try to fetch a row and then use v_cursor%NOTFOUND to determine of the cursor is empty:
DECLARE
v_cursor SYS_REFCURSOR;
v_value DUAL.DUMMY%TYPE;
BEGIN
OPEN v_cursor FOR SELECT DUMMY FROM DUAL WHERE 1 = 0;
FETCH v_cursor INTO v_value;
IF v_cursor%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Cursor empty');
ELSE
DBMS_OUTPUT.PUT_LINE('Cursor not empty');
END IF;
END;
/
or
DECLARE
CURSOR v_cursor IS
SELECT DUMMY FROM DUAL WHERE 1 = 0;
v_row v_cursor%ROWTYPE;
BEGIN
OPEN v_cursor;
FETCH v_cursor INTO v_row;
IF v_cursor%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Cursor empty');
ELSE
DBMS_OUTPUT.PUT_LINE('Cursor not empty');
END IF;
END;
/
Both output:
Cursor empty
db<>fiddle here

How to execute dynamic sql into cursor in Oracle?

I have problem with execute dynamic sql statement into sys_refcursor in my stored procedure.
I have looked in documentation and I think that I build my procedure properly, but I still do not know why error occurs.
Please look below, what I created:
CREATE TABLE REKOM_CROSS_PROM (
LINIA_PROD VARCHAR2(20),
ID_REKOM_OFERTA VARCHAR2(20),
PRICE NUMBER,
MAX_PRICE NUMBER
);
/
CREATE OR REPLACE TYPE prodType AS OBJECT (
p_line VARCHAR2(20)
,p_price NUMBER
);
/
CREATE OR REPLACE TYPE prodTypeList IS TABLE OF prodType;
/
CREATE OR REPLACE PROCEDURE my_proc (prodLines IN prodTypeList ,rekom OUT SYS_REFCURSOR)
IS
v_pLine VARCHAR2(20);
v_query VARCHAR2(4000);
BEGIN
v_query := 'SELECT ID_REKOM_OFERTA FROM REKOM_CROSS_PROM WHERE
LINIA_PROD=NULL';
FOR i IN 1 .. prodLines.COUNT
LOOP
v_pLine := prodLines(i).p_line;
v_query := v_query || ' UNION ALL SELECT ID_REKOM_OFERTA FROM
REKOM_CROSS_PROM WHERE LINIA_PROD=''' || v_pLine || '''';
END LOOP;
OPEN rekom FOR v_query;
END my_proc;
/
And when I want to call the procedure, error occur.
set serveroutput on
declare
type1 prodTypeList := prodTypeList(prodType('test1',1), prodType('test2', 20));
rc SYS_REFCURSOR;
row varchar2(200);
BEGIN
MY_PROC(type1, rc);
fetch rc into row;
while (rc%found) loop
dbms_output.put_line(row);
end loop;
close rc;
end;
I get the message:
ORA-20000: ORU-10027: buffer overflow, limit of 1000000 bytes
*Cause: The stored procedure 'raise_application_error'
was called which causes this error to be generated.
*Action: Correct the problem as described in the error message or contact
the application administrator or DBA for more information.
Can sombody help me to resolve the problem?
You have an infinite loop. That means you're calling dbms_output.put_line forever - or until it runs out of buffer space, and throws that exception.
BEGIN
MY_PROC(type1, rc);
-- fetch first row from result set
fetch rc into row;
-- check if last fetch found something - always true
while (rc%found) loop
dbms_output.put_line(row);
end loop;
close rc;
end;
Every time around the loop you're checking the result of that first fetch, which stays true (assuming there is any data). You need to fetch each time round the loop:
BEGIN
MY_PROC(type1, rc);
-- fetch first row from result set
fetch rc into row;
-- check if last fetch found something
while (rc%found) loop
dbms_output.put_line(row);
-- fetch next row from result set
fetch rc into row;
end loop;
close rc;
end;
Or perhaps more commonly, only fetch inside the loop, and stop when nothing is found, using %notfound rather than %found:
BEGIN
MY_PROC(type1, rc);
loop
-- fetch row from result set
fetch rc into row;
-- check if last fetch found something
exit when rc%notfound;
dbms_output.put_line(row);
end loop;
close rc;
end;
Not related to your current issue, but the predicate WHERE LINIA_PROD=NULL is never true; null isn't equal to (or not equal to) anything else. You need to use WHERE LINIA_PROD IS NULL instead.

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

Dynamic SQL LOOP

Dynamic SQL is not my friend, basically the idea is that I can use the procedure with the "p_in_table" paramter to get the number of rows contained in the table.
CREATE OR REPLACE PROCEDURE how_many_rows(p_in_table VARCHAR2)
IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
BEGIN
v_cur_txt := 'SELECT * FROM ' || p_in_table;
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
dbms_output.put_line(v_rowcount);
END;
Would preciate it if someone would tell me what am I doing wrong?
The problem is that you not iterating through cursor - no fetch statement or something like that, so, basically, you have an infinite loop. To avoid this you need to do something like this:
CREATE OR REPLACE PROCEDURE how_many_rows
(p_in_table VARCHAR2) IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
v_row SOME_TABLE%ROWTYPE; --add row variable
BEGIN
v_cur_txt := 'SELECT * FROM '|| p_in_table;
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
FETCH v_cur_cur INTO v_row; --fetch a row in it
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
DBMS_OUTPUT.PUT_LINE(v_rowcount);
END;
But, as you can see, to do this you need to know, what table you're quering, so this is not general solution. Maybe there is a workaround for this, but i suggest, you use more simple and efficient approach, for example with EXECUTE IMMEDIATE:
CREATE OR REPLACE PROCEDURE HOW_MANY_ROWS(p_in_table VARCHAR2)
IS
v_tmp NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM ' || p_in_table INTO v_tmp;
DBMS_OUTPUT.PUT_LINE(v_tmp);
END;
Ok, I gave a thought on how to achieve this using your way, and here is what i've ended up with - just fetch ROWNUM from your table, every table has it and you know it's type - NUMBER. So this procedure will work in general case:
CREATE OR REPLACE PROCEDURE how_many_rows
(p_in_table VARCHAR2) IS
TYPE cur_cur IS REF CURSOR;
v_cur_cur cur_cur;
v_rowcount NUMBER(28);
v_cur_txt VARCHAR2(299);
v_row NUMBER; --add rownum variable
BEGIN
v_cur_txt := 'SELECT ROWNUM FROM '|| p_in_table; --select only rownum from target table
OPEN v_cur_cur FOR v_cur_txt;
LOOP
v_rowcount := v_cur_cur%ROWCOUNT;
FETCH v_cur_cur INTO v_row; --fetch rownum in it
EXIT WHEN v_cur_cur%NOTFOUND;
END LOOP;
CLOSE v_cur_cur;
DBMS_OUTPUT.PUT_LINE(v_rowcount);
END;

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;