how to check if SYS_REFCURSOR is empty - sql

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

Related

How to write Execute immediate in cursor select Query

How to wite EXECUTE IMMEDIATE in the cursor select Query.
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
CURSOR cur_attendee_data
IS
EXECUTE IMMEDIATE l_cur_query;
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
here I try to use initialize cursor outside begin block but it rises exception, how to write a dynamic query for the cursor,
cur_attendee_data is
EXECUTE IMMEDIATE l_cur_query;
Error(113,8): PLS-00103: Encountered the symbol "CUR_ATTENDEE_DATA" when expecting one of the following: := . ( # % ;
If you fetch all rows with BULK COLLECT INTO ... then you need only one loop, the second loop is useless.
The basic solution would be this one:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from {table X} where c1=:d';
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
However, I don't see any reason to make dynamic SQL. You can simply run
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE) IS
cur_attendee_data SYS_REFCURSOR;
TYPE rec_attendee_data IS TABLE OF {table X}%ROWTYPE;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR SELECT * from {table X} where c1 = in_from_date;
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
FOR i IN 1 .. l_cur_attendee_data.COUNT LOOP
-- do whatever you like to do with l_cur_attendee_data(i)
END LOOP;
CLOSE cur_attendee_data;
END;
You can use below code instead -
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (100) := 'SELECT * from table X where c1=:in_from_date';
TYPE t_cur IS REF CURSOR;
cur_attendee_data t_cur
TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
INDEX BY PLS_INTEGER;
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data FOR l_cur_query USING in_from_date;
LOOP
FETCH cur_attendee_data BULK COLLECT INTO l_cur_attendee_data;
EXIT WHEN l_cur_attendee_data.COUNT = 0;
DBMS_OUTPUT.put_line ('here in first insert');
lrec := return_attendee_report ();
out_attendee_tab :=
return_attendee_arr_result (return_attendee_report ());
out_attendee_tab.DELETE;
FOR i IN 1 .. l_cur_attendee_data.COUNT
LOOP
BEGIN
NULL;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error occurred : ' || SQLERRM);
END;
END LOOP;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('HERE INSIIDE OTHERS' || SQLERRM);
END;
You need to use open cursor for '' as following:
CREATE OR REPLACE PROCEDURE biq_attendee_report (in_from_date IN DATE)
IS
l_cur_query VARCHAR2 (5000) := 'SELECT * from table X where c1='|| in_from_date;
cur_attendee_data SYS_REFCURSOR; -- JUST DECLARED THE CURSOR
--TYPE rec_attendee_data IS TABLE OF cur_attendee_data%ROWTYPE
-- INDEX BY PLS_INTEGER; -- this declaration must be at schema level
l_cur_attendee_data rec_attendee_data;
BEGIN
OPEN cur_attendee_data for l_cur_query; -- OPEN THE CURSOR WITH DYNAMIC QUERY
..
.. -- YOUR CODE AS IT IS
..
Cheers!!

Write with cursor into a table (table name as parameter)

How could I insert in a table using CURSOR with a name gives as a parameter?
Thanks
PROCEDURE delta (pTableName IN VARCHAR2, pStichTag IN DATE) IS
lTabName := VARCHAR(30);
v_stmt_str := VARCHAR(4000);
cCursor SYS_REFCURSOR;
BEGIN
lTabName := substr(pTableName,5);
v_stmt_str := 'SELECT * FROM '|| lTableName ||' WHERE dwh_date =
to_date('||pStichTag||','DD.MM.YY');
OPEN cCursor FOR v_stmt_str USING 'MANAGER';
LOOP
FETCH cCursor INTO pTableNAME%ROWTYPE;
-- UPATE pTableName SET some WHERE this line
END LOOP;
CLOSE cCursor;
You need to use EXECUTE IMMEDIATE to perform the update:
EXECUTE IMMEDIATE 'UPDATE ' || pTableName ||
' SET SOME_COLUMN = 12345 WHERE SOME_OTHER_COLUMN = ''xyz''';
However, you're going to have to re-do your cursor logic. Because you don't know the table name you don't know which fields will be fetched prior to opening the cursor, so you're going to have to hard-code the field names to be fetched instead of using *. If that's not to your liking you'll have to use the DBMS_SQL package, which allows for more flexibility - but I'll warn you, it's rather complex to use.
Corrected code snippet
PROCEDURE delta (pTableName IN VARCHAR2, pStichTag IN DATE)
IS
lTabName VARCHAR(30); -- removed :=
v_stmt_str VARCHAR(4000); -- removed :=
cCursor SYS_REFCURSOR;
BEGIN
lTabName := substr(pTableName,5); -- I dont understand why only first five digits of table name is used. May be your logic
v_stmt_str := 'SELECT * FROM '|| lTableName ||' WHERE dwh_date =
to_date('''||pStichTag||''',''DD.MM.YY'')'; -- added few ' for proper string concatenation
OPEN cCursor FOR v_stmt_str USING MANAGER; -- removed ' from MANAGER, MANAGER must be some variable and for your cusrsor, there is no bind variable so there is no need of USING clause at all
-- You can use just: OPEN cCursor FOR v_stmt_str;
LOOP
FETCH cCursor INTO pTableNAME%ROWTYPE;
-- UPATE pTableName SET some WHERE this line
END LOOP;
CLOSE cCursor;
I hope this will be useful.
Cheers!!

Oracle Sql selecting all data from a list of views

I am trying to write a sql to select all data from a list of views for a particular view.
I'm getting all the user view this way:
select view_name from user_views
Say the output is:
emp_v
dept_v
countries_v
jobs_v
Is it possible for me to pass one of these views as parameter in the original sql(which is pulling all the views) so i can get all the data in this view?
Thanks
This might be what you are looking for. This will loop those views and select everything in them. NOTE: This can cause a lot of DBMS_OUTPUT. I suggest hardcoding the cursor for a specific view first to make sure it is what you are looking for.
(1) Create this procedure...
CREATE OR REPLACE procedure print_view(p_query in varchar2) is
l_theCursor integer default dbms_sql.open_cursor;
l_columnValue varchar2(4000);
l_status integer;
l_descTbl dbms_sql.desc_tab;
l_rowCnt number := 0;
l_colCnt number;
begin
dbms_sql.parse(l_theCursor, p_query, dbms_sql.native);
dbms_sql.describe_columns(l_theCursor, l_colCnt, l_descTbl);
dbms_output.put_line(l_colCnt);
for i in 1 .. l_colCnt loop
dbms_sql.define_column(l_theCursor, i, l_columnValue, 4000);
end loop;
l_status := dbms_sql.execute(l_theCursor);
while(dbms_sql.fetch_rows(l_theCursor) > 0) loop
l_rowCnt := l_rowCnt +1;
dbms_output.put_line('========== ROW '||l_rowCnt||' ==========');
for i in 1 .. l_colCnt loop
dbms_sql.column_value(l_theCursor, i, l_columnValue);
dbms_output.put_line(rpad(l_descTbl(i).col_name, 30)||': '||substr(l_columnValue, 1, 200));
end loop;
end loop;
end print_view;
/
(2) Then run this...
declare
/*
NOTE: Edit the where clause. You should try this with just a single view first to make sure this is what you want.
*/
cursor cursor1 is
select view_name from user_views
where view_name in ('emp_v','dept_v','countries_v','jobs_v');
begin
for c1 in cursor1 loop
print_view('select * from '|| c1.view_name); /* Pass the view as a parameter like requested. */
end loop;
end;
/

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

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;

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;