I have a procedure that takes a SYS_REFCURSOR and converts it to JSON.
In a procedure that calls the above, I'm trying define a CURSOR as normal and supply that as a REF CURSOR.
I'm receiving PLS-00361.
I understand that I can use the OPEN FOR construct, but I need to use my Cursor elsewhere and don't like the duplication.
Any advice?
PROCEDURE LIST_EMPLOYEES
AS
l_ref_cursor SYS_REFCURSOR;
CURSOR c_emps
IS
SELECT email_address
FROM employees;
BEGIN
OPEN c_emps;
FETCH c_emps INTO l_ref_cursor;
json_utils.refcursor_to_json_via_http(l_ref_cursor,
'employees');
CLOSE l_ref_cursor;
EXCEPTION
WHEN others
THEN
log_error;
END LIST_EMPLOYEES;
Regards,
Laurence.
You wouldn't fetch the cursor into the REF CURSOR, you would simply OPEN it:
PROCEDURE LIST_EMPLOYEES AS
l_ref_cursor SYS_REFCURSOR;
BEGIN
OPEN l_ref_cursor FOR SELECT email_address FROM employees;
json_utils.refcursor_to_json_via_http(l_ref_cursor, 'employees');
CLOSE l_ref_cursor;
END LIST_EMPLOYEES;
Related
please, what is bad in this procedure. Mistake is PLS-00103 in 8,9 row
create or replace PROCEDURE test_one(l_f_name VARCHAR2,l_l_name VARCHAR2)
IS
CURSOR c1(f_name VARCHAR2,l_name VARCHAR2) IS
SELECT lastname,firstname
FROM CUSTOMER;
v_complex c1%ROWTYPE;
f_name = l_f_name;
l_name = l_l_name;
BEGIN
open c1(f_name,l_name);
fetch c1 into v_complex;
dbms_output.put_line(v_complex.lastname|| ' ' ||v_complex.firstname);
exit when c1%notfound;
close c1;
end;
Assignment operator in PL/SQL is :=. Additionally type is missing in the local variable declaration (thanks to #Nitish). So valid code is:
f_name VARCHAR2(50) := l_f_name;
l_name VARCHAR2(50) := l_l_name;
Explicit cursors like this one are to be avoided in PLSQL.
It should be:
FOR v_complex IN c1(l_f_name, l_l_name) LOOP
dbms_output.put_line(v_complex.lastname|| ' ' ||v_complex.firstname);
END LOOP;
In which case you don't need to declare v_complex at all, or f_name, or l_name. Also, the cursor does not appear to actually use these variables. What are you trying to do?
This implicit cursor also avoids the need for explicit OPEN and CLOSE statements, you'll never get cursor leaks this way.
As for the actual problem, if the initialization is fixed, then your DBMS_OUTPUT should be after the %NOTFOUND check.
Here is what I have in my stored procedure
CREATE OR REPLACE PROCEDURE GET_USER (IN IN_USER_NAME VARCHAR(256))
DYNAMIC RESULT SETS 1
P1: BEGIN
-- Declare cursor
DECLARE cursor1 CURSOR WITH RETURN for
IF (IN_USER_NAME IS NULL) THEN
SELECT BLUEMSP.USERS.USER_ID FROM BLUEMSP.USERS;
END IF;
-- Cursor left open for client application
OPEN cursor1;
END P1
I am getting an error on the If statement line that says
Multiple markers at this line
"." was expected instead of "(".
- "JOIN" was expected instead of "THEN".
- "IF (IN_USER_NAME IS NULL) THEN
SELECT BLUEMSP.USERS.USER_ID FROM BLUEMSP." appears to be misplaced.
- "(" was expected after "IS".
Why is this not working?
Thanks
You can't use an IF statement in a cursor declaration, as IF is PL/SQL, and a cursor declaration must be pure SQL.
CREATE OR REPLACE PROCEDURE GET_USER (IN_USER_NAME IN VARCHAR2)
IS
xxx BLUEMSP.USERS.USER_ID%TYPE;
CURSOR cursor1 IS SELECT BLUEMSP.USERS.USER_ID FROM BLUEMSP.USERS;
BEGIN
IF (IN_USER_NAME IS NULL) THEN
OPEN cursor1;
FETCH cursor1 INTO xxx;
...
END IF
CLOSE cursor1;
END
My whole intention to catch exception,WRONG parameter is NOT CATCHING exception.
Here is the code:
CREATE OR REPLACE PROCEDURE list_emp (p_emp_id IN employees.employee_id%TYPE,
p_dept_id IN employees.department_id%TYPE)
IS
CURSOR c1 IS
SELECT *
FROM EMPLOYEES
WHERE EMPLOYEE_ID=p_emp_id
AND DEPARTMENT_ID=p_dept_id;
emp_rec c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO emp_rec;
EXIT WHEN c1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
END LOOP;
CLOSE c1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('No Record Found ');
END;
When the cursor is opened and fetched with the wrong parameter that does not match any row from the corresponding table, the following line
EXIT WHEN c1%NOTFOUND;
cause the plsql procedure to terminate (because there were no rows found). Hence no exception is raised.
If you do want to display some sort of output you can do the following instead
IF c1%FOUND THEN
dbms_output.put_line('Record Found');
ELSE
dbms_output.put_line('Finished/Done');
EXIT;
END IF;
If you want to raise an error after looping through a cursor that returns no rows, then you're going to have to use a counter to work out how many rows have been processed, and then you can do something if no rows have been processed.
Something like:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
cursor c1 is
select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
v_count number := 0;
begin
for emp_rec in c1
loop
v_count := v_count + 1;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
end loop;
if v_count = 0 then
raise no_data_found;
end if;
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
A few notes:
I converted your cursor loop into a cursor-for-loop; you don't need to worry about declaring the record type and also Oracle handles the opening and closing of the cursor for you.
I added raise; to each of your exception handlers - in general, having when others then null (which is effectively what your original code was doing - no errors are raised to the calling code) is a bad idea. I added the raise to the no_data_found condition as that wasn't doing anything either; typically, if you have an exception condition, you want it to do something to let the calling code know there was a problem (not always, of course; sometimes you don't want the processing to stop if a particular error condition is met).
Your cursor was selecting all columns, but in your procedure, you were only using three of them. I've therefore amended the cursor so that it only pulls back those three columns.
Don't rely on dbms_output in your production code. Code that calls this procedure won't see anything populated in dbms_output, unless it explicitly looks for it - and that's not something I've ever seen in any production code, outside of Database tools (eg. SQL*Plus, Toad, etc). I've left this in your procedure as I've a feeling this is a learning exercise for you, but please don't think that this is in any way acceptable in production code.
You're passing p_emp_id in as a parameter - typically, that's the primary key of the employees table. If that's the case, then there's no need for the cursor for loop at all - you could do it by using select ... into ... instead, like so:
.
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type)
is
v_emp_id employees.employee_id%type;
v_first_name employees.first_name%type;
v_last_name employees.last_name%type;
begin
select employee_id,
first_name,
last_name
into v_emp_id,
v_first_name,
v_last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
dbms_output.put_line(emp_rec.employee_id||' '||emp_rec.first_name||' '||emp_rec.last_name);
exception
when no_data_found then
dbms_output.put_line('No Record Found.');
raise;
when others then
dbms_output.put_line('An error occurred: '||sqlerrm);
raise;
end;
/
Alternatively, just pass back a ref cursor:
create or replace procedure list_emp (p_emp_id in employees.employee_id%type,
p_dept_id in employees.department_id%type,
p_ref_cur out sys_refcursor)
is
begin
open p_ref_cur for select employee_id,
first_name,
last_name
from employees
where employee_id = p_emp_id
and department_id = p_dept_id;
-- No need for an exception handler here since you're not storing the error details anyway.
-- By not having an error handler, any error will automatically be raised up to the calling code
-- and it will have the correct error stack trace info (e.g. the line number the error occurred,
-- rather than the line the error was reraised from
end;
/
And to run the ref cursor in SQL*Plus (or as a script in Toad/SQL Developer/etc), you do the following:
-- create a variable outside of PL/SQL to hold the ref cursor pointer (this is a SQL*Plus command):
variable rc refcursor;
-- populate our ref cursor variable with the pointer. Note how we pass it in as a bind variable
begin
list_emp(p_emp_id => 1234,
p_dept_id => 10,
p_ref_cur => :rc);
end;
/
-- finally, print the contents of the ref cursor.
print rc;
I am working on a package that will return two cursors. One cursor is a list of items with a numeric primary key. The other cursor is a list of files associated with the items
Code so far:
procedure get_items_with_files(
o_results out sys_refcursor,
o_files out sys_refcursor
) is
begin
begin
open o_results for
select item_id,
item_name
from items;
end;
begin
open o_files for
select item_id
item_file_name
from item_files if
where if.item_id in (select item_id from TABLE(CAST(o_results)));
end;
end get_items_with_files;
The areas I am running into problems with:
Getting a missing keyword error on the table(cast(cursor)) section
Can I access the cursor in the code as I am or do i need to copy it to an internal variable? I tried to create a variable of sys_refcursor type and a "set v_cursor := o_results" but got a missing or invalid option error.
You can't use the O_RESULTS cursor to open the O_FILES cursor.
You can query the ITEMS table in order to open both cursors but that introduces the possibility that some data changes between when you open the O_RESULTS cursor and the time you open the O_FILES cursor and that the two result sets are out of sync.
procedure get_items_with_files(
o_results out sys_refcursor,
o_files out sys_refcursor
) is
begin
begin
open o_results for
select item_id,
item_name
from items;
end;
begin
open o_files for
select item_id
item_file_name
from item_files if
where if.item_id in (select item_id from items);
end;
end get_items_with_files;
It would be much more common to return a single cursor that represents the result of joining the two tables
procedure get_items_with_files(
o_results out sys_refcursor
) is
begin
open o_results for
select item_id,
item_name,
item_file_name
from items
join item_files using (item_id);
end get_items_with_files;
If all your procedure is doing is opening a cursor, however, it would be more common to create a view rather than creating a procedure and then query the view rather than calling the procedure.
I created a plsql function and I want to create a cursor and return this cursor from the function. Then I want to call this function in a Java class and retrieve data from the cursor. Note: the cursor returns one row.
I wrote something like this,,
CREATE OR REPLACE
FUNCTION FUNCTION1 ( emp_id IN NUMBER)RETURN cursor AS
cursor newCursor(e_id number) is
select * from table1 where employee_id = e_id;
type refCursor is ref cursor;
BEGIN
open newCursor(emp_id);
loop
exit when newCursor%notfound;
fetch newCursor into refCursor;
end loop;
RETURN refCursor;
END FUNCTION1;
What return type should I use if I want to return a cursor?
Model it after the following function which works
create or replace function getemps return sys_refcursor is
v_curs sys_refcursor;
begin
open v_curs for select ename from emp;
return v_curs;
end;
/
sys_refcursor is oracle's generic implicit cursor
use EXPLICITE cursor instead