Using 'open cursor' as 'execute immediate' - sql

I am trying to use open cursor as a substitute for execute immediate because my SQL statement could return multiple records.
open cur1 for rule_sql;
loop
dbms_output.put_line(cur1.rule_id);
end loop;
close cur1;
It throws an error saying: "PLS-00487: Invalid reference to variable 'CUR1'"
Has anyone had similar issues? Any help is much appreciated :)

The cursor is just a pointer to a result set. To reference its contents you need to fetch it into a variable. Note that the variable must be a record type which matches the projection of the query. This may be hard if you're using dynamic SQL to implement a fluid set of columns.
Anyway, something like this:
declare
cur1 sys_refcursor;
Type cur_rec is record (
rule_id number,
rule_desc varchar2(32));
row1 cur_rec;
....
Begin
...
open cur1 for stmt;
for row1 in cur1 loop
Dbms_output.put_line(row1.rule_id);
End loop;
....
End;
"If I do not know the type of columns in the result then I cannot create a variable to capture the cursor values."
Life is more complicated when you don't know the projection of your query at compile time. You can't use Native Dynamic SQL anymore, you need to go full DBMS_SQL.
In 11g Oracle introduced the so-called Method 4 Dynamic SQL. This allows us to handle variable projections at the cost of a lot more code. Adrian Billington wrote an excellent introduction to this on his Oracle-developer.net site. Check it out

You have missed the fetch statement - see Example 7.4 in docs
open cur1 for rule_sql;
loop
fetch cur1 into my_row_variable;
exit when cur1%notfound;
dbms_output.put_line(cur1.rule_id);
end loop;
close cur1;

Related

OPEN emp_refcur FOR {plsql variable} giving error

I am novice in plsql development.
I have a problem opening cursor on plsql variable (which has my dynamic SQL query). I am on ORACLE 12 i believe.
Details below:
emp_refcur SYS_REFCURSOR; -- Cursor declaration
component_sql:= 'select * from emp'; -- Base SQL
-- dynamic sql based on business logic
IF SOME_CONDITION THEN
filters:= ' where DEP IN (''ABC'',''DEF'')';
ELSE
filters:= ' where age>50';
component_sql:= component_sql||filters; -- appending filter to my base sql
OPEN emp_refcur FOR component_sql; -- opening the cursor
LOOP
FETCH emp_refcur INTO result;
EXIT WHEN emp_refcur%NOTFOUND;
dbms_output.put_line(result);
END LOOP;
CLOSE emp_refcur;
Compiling this procedure shown error at line 'OPEN emp_refcur FOR component_sql' and error are
Error(90,9): PL/SQL: Statement ignored
Error(90,29): PLS-00382: expression is of wrong type
I do not want to use bind values like
'OPEN emp_refcur FOR p_query_string USING p_deptno, p_sal;'
I have tried multiple approaches
OPEN emp_refcur FOR ''||component_sql||''; -- This approach is not causing compilation error, but running the procedure resulting
ORA-00900: invalid SQL statement
AND
OPEN emp_refcur FOR 'select * from emp'||filters; -- This is resulting the error
ORA-00900: invalid SQL statement
Not sure what i am missing here. Please help.
NOTE: Please ignore if there are any SQL query syntax errors. Because the SQL which i am printing after appending the filters is executing fine and getting results when i run separately.
I tried with your code and it has no issues. PLS-00382: expression is of wrong type --> This is a syntax error. It has nothing to do with bind variables. What is the declaration for result? I don't see in the snippet.
PLS-00382: expression is of wrong type
Cause: An expression has the wrong datatype for the context in which it was found.
Action: Change the datatype of the expression. You might want to use datatype conversion functions.
Declare
emp_refcur SYS_REFCURSOR;
filters varchar2(100);
component_sql varchar2(100);
result varchar2(20);
Begin
component_sql:= 'select 1 from dual'; -- Base SQL
-- dynamic sql based on business logic
IF 1=1 THEN
filters:= ' UNION ALL SELECT 2 from dual';
ELSE
filters:= ' where age>50';
End if;
component_sql:= component_sql||filters; -- appending filter to my base sql
OPEN emp_refcur FOR component_sql ; -- opening the cursor
LOOP
FETCH emp_refcur INTO result;
EXIT WHEN emp_refcur%NOTFOUND;
dbms_output.put_line(result);
END LOOP;
CLOSE emp_refcur;
END;

Does Oracle close the SYS_REFCURSOR returned from a function which is used in a SELECT statement?

I need to store some queries in the DB (inside functions in packages) and then call the functions from SQL Developer (from ORDS), so I found that you can return the queries in SYS_REFCURSORs from the stored functions like this:
CREATE OR REPLACE FUNCTION test RETURN SYS_REFCURSOR AS
vRC SYS_REFCURSOR;
BEGIN
OPEN vRC FOR SELECT *
FROM employees
WHERE empid = 34650;
RETURN vRC;
END;
And later on, simply retrieve the data in SQL Developer like this:
SELECT test
FROM dual;
So my question is...
Is this correct? I know that everytime we OPEN a cursor we need to explicitaly CLOSE it and every example of this that I have foud close the refcursor in PL/SQL and I need to get the data from a SELECT statement (in order to parse it to JSON in SQL DEVELOPER).
Also I have found that in SQLPlus the "print" statement closes the cursor once it fetched all the data, does the SELECT statement im my example do this as well?
You can get the cursor and print its contents like this:
VARIABLE cur REFCURSOR;
BEGIN
:cur := test();
END;
/
PRINT cur;
The PRINT and the SELECT statements will both read the cursor and when they have read all the rows they will implicitly close the cursor. You cannot read the data from a cursor twice (however, you can call the function multiple times to get multiple cursors all containing the same information).

PL/SQL dynamic row printing?

I'm a newbie in SQL and I was wondering if you can print the contents of a cursor%rowtype dynamically ?
For example:
cursor cur is select * from ...;
current_row cur%rowtype;
begin
open cur;
loop
fetch cur into current_row;
-- dbms_output.put_line( current_row ); Would that be possible ?
exit when cur%notfound;
end loop;
Or do I have to do the boring part myself, that is specifying each member I need to print, which is quite lousy and lame, e.g.:
dbms_output.get_line( current_row.first || current_row.second || .... || current_row.last );
Is there a simple way to achieve dynamic printing to some extent in PL/SQL? Im using PL/SQL developer.
Thanks
Use the DBMS_SQL package to archive that. Of course it takes more lines (than the simple %rowtype cursor loop in your example) to create and loop through the cursor, but once this is done, you can process any SELECT query and output the results, no matter how many columns the query returns of what types the columns have.

Check Values in sys_refcursor

I have the following code in a function
CREATE OR REPLACE FUNCTION my_func (
v_dt events.raised_date%TYPE
)
RETURN SYS_REFCURSOR
IS
p_events SYS_REFCURSOR;
OPEN p_events FOR
SELECT event_id
FROM events
WHERE raised_date = v_dt;
RETURN p_events;
END;
I would like to check whether 100 exists in p_events cursor or not. How can I do this inside my function.
Any help is highly appreciable.
A ref cursor is just a pointer to query. There is nothing "in" it. So the only way to find out whether the result set identified by the ref cursor contains a specific record - or indeed any records - is to fetch the cursor and read through the records.
Bear in mind that a ref cursor is a one-shot thang. We cannot fetch the same cursor more than once. We have to close and re-open it. But that means we run the risk of the second fetched result set differing from the first (unless we change the transaction's isolation level).
So the upshot is, just code the consuming procedure to fetch and use the ref cursor, and make sure it handles both the presence and absence of interesting records.
It is not good idea to check it inside of the function. You are missing why the cursor is returned. Instead do it outside of the function.
DECLARE
l_rc SYS_REFCURSOR := my_func();
TYPE events_ntt IS TABLE OF NUMBER;
l_events events_ntt;
l_lookup events_ntt := events_ntt(100);
l_diff events_ntt;
BEGIN
FETCH l_rc BULK COLLECT INTO l_events;
l_diff := l_events MULTISET INTERSECT DISTINCT l_lookup;
IF l_diff.COUNT > 0 THEN
DBMS_OUTPUT.PUT_LINE('100 EXISTS');
ELSE
DBMS_OUTPUT.PUT_LINE('100 DOES NOT EXIST');
END IF;
END;
Using Cursor Variables (REF CURSORs)
Like a cursor, a cursor variable points to the current row in the
result set of a multi-row query. A cursor variable is more flexible
because it is not tied to a specific query. You can open a cursor
variable for any query that returns the right set of columns.
You pass a cursor variable as a parameter to local and stored
subprograms. Opening the cursor variable in one subprogram, and
processing it in a different subprogram, helps to centralize data
retrieval. This technique is also useful for multi-language
applications, where a PL/SQL subprogram might return a result set to a
subprogram written in a different language, such as Java or Visual
Basic.
What Are Cursor Variables (REF CURSORs)?
Cursor variables are like pointers to result sets. You use them when
you want to perform a query in one subprogram, and process the results
in a different subprogram (possibly one written in a different
language). A cursor variable has datatype REF CURSOR, and you might
see them referred to informally as REF CURSORs.
Unlike an explicit cursor, which always refers to the same query work
area, a cursor variable can refer to different work areas. You cannot
use a cursor variable where a cursor is expected, or vice versa.
Source: http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/sqloperations.htm#i7106
(Oracle Database PL/SQL User's Guide and Reference)
It can be done like this
declare
evt EVENTS%ROWTYPE;
found_100 boolean := false;
begin
loop
fetch p_events into evt;
exit when p_events%NOTFOUND;
if evt.event_id = 100 then
found_100 := true;
exit;
end if;
end loop;
end;
but however it's very inefficient because you're possibly fetching millions of records where you actually only need 1 fetch.

oracle - out sys_refcursor parameter - if no result found then?

If I use out sys_refcursor parameter, I'm struggling with if the first cursor has any results.
create or replace procedure cursorresults
as
(cursor1 OUT sys_refcursor
cursor2 OUT sys_Refcursor)
begin
open cursor1 for
select * from table1;
**if cursor1 has any results**
open cursor2 for
select * from table2;
end;
I can requery the table1 to see if there was a match but there must be a better way?
The only way to determine whether a cursor returns results is to attempt to fetch results from the cursor. Of course, since a cursor is a forward-only structure, that means that you would need to close and re-open the cursor.
It seems very odd, though, that you would only want to open the second cursor if the first cursor returns results. Are you sure that you don't want to join the two tables, union them, or do something else to combine the two results?