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.
Related
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;
i'm having one procedure which returns setof cursors
Now i have to call that procedure to another procedure and access the data
that return by that procedure
is their any way to do this in postgres.
This is code for 1st procedure,
CREATE OR REPLACE FUNCTION public.returns_multiple_cursor( )
RETURNS SETOF refcursor
LANGUAGE 'plpgsql'
COST 100.0
AS $function$
DECLARE
_daily refcursor := 'first_cur';
_fac_hourly refcursor := 'second_cur';
BEGIN
open first_cur for
select * from x;
return next first_cur;
open second_cur for
select * from y;
return second_cur;
END
$function$;
ALTER FUNCTION public.returns_multiple_cursor();
Here code for other second procedure
CREATE OR REPLACE FUNCTION public.access_cursor( )
RETURNS SETOF refcursor
LANGUAGE 'plpgsql'
COST 100.0
AS $function$
DECLARE
BEGIN
-- what code will be here to access the cursor data in this procedure
select public.returns_multiple_cursor();
END;
ALTER FUNCTION public.access_cursor();
Unfortunately, you cannot use the FOR <recordvar> IN <cursor> loop, because it only works for bound cursors (which refcursors are not).
But you can still loop through them, with the old-fashioned FETCH:
declare
rec record;
cur refcursor;
begin
for cur in select returns_multiple_cursor() loop
loop
fetch next from cur into rec;
exit when not found;
-- do whatever you want with the single rows here
end loop;
close cur;
end loop;
end
Unfortunately, there is still another limitation: PostgreSQL caches the first cursor's plan (at least, it seems it does something like that), so you must use cursors, which uses the same column types (you'll have to use the same column names anyway, to be able to refer them in the inner loop, like rec.col1).
Complete, working example: http://rextester.com/FNWG91106 (see f.ex. what happens, when you remove casting from the cursors' queries).
If you have fix number of cursors (like in your example), but differently structured underlying queries, it might be easier to declare your returns_multiple_cursor as:
create or replace function returns_multiple_cursor(out first_cur refcursor,
out second_cur refcursor)
-- returns record (optional)
language plpgsql
-- ...
This way, you could access your cursors more directly in the calling context.
Update: it seems that when you don't use explicit column names, just generic record processing (via f.ex. JSON or hstore), plan caching does not cause any trouble:
http://rextester.com/QHR6096
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).
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?
I want to return a cursor from a function, I have read that I can use:
return sys_refcursor
And then
open curs for select* from mytable;
return curs;
I tried curs.att := 'something' but I get an error
Also read I can do my own type:
TYPE type IS REF CURSOR RETURN mytable%ROWTYPE;
Then
CURSOR cur IS
SELECT* FROM mytable;
var cur%ROWTYPE;
BEGIN
OPEN cur;
FETCH cur INTO var;
var.att = 'something';
RETURN var;
This time I didn't get an error in the assign but in the return statement.
If I changed the var type to my type I couldn't fetch the value.
I wan't to edit the cursor, but not the table, how can I do this?
A cursor is a read-only structure. The only way to change the data that you would fetch from a cursor is to change the SQL statement that is used to open the cursor or to change the data in the underlying table(s).
While it is possible to return a cursor from one PL/SQL block to another, it is rarely the appropriate architecture. A SYS_REFCURSOR is generally appropriate when you want to return a result to a client application that knows how to use a cursor.
Do you really want to return a cursor, though? Or do you want to return a record type? The second code snippet you posted appears to be trying to return a record-- that's certainly possible but you would need to declare that the function returns a record rather than a cursor. That is, the RETURN statement in the declaration would need to be RETURN mytable%ROWTYPE rather than RETURN type. For example, if you want to return a record based on the EMP table
create or replace function get_emp( p_empno in emp.empno%type )
return emp%rowtype
is
l_rec emp%rowtype;
begin
select *
into l_rec
from emp
where empno = p_empno;
l_rec.sal := l_rec.sal + 100;
return l_rec;
end;