How to close SYS_REF CURSOR in oracle pl/sql? - plsql

I can't compile this pl/sql. I am not familiar with pl/sql and I want to close c_cur after using it.
create or replace
procedure GET_INSPECTION_CALL_REF_NO (
in_vendor_code varchar2,
c_cur out SYS_REFCURSOR
)
is
declare
c_cur is
select call_log_id, call_log_ref_no
from INSPECTION_CALL_LOG
where modified_by = in_vendor_code;
var_c_cur c_cur%ROWTYPE;
begin
open c_cur;
loop
fetch c_cur into var_c_cur
exit when c_cur%notfound;
end loop;
close c_cur;
end;
end GET_INSPECTION_CALL_REF_NO;

You can rewrite your procedure like,
create or replace
procedure GET_INSPECTION_CALL_REF_NO (
in_vendor_code varchar2,
c_cur out SYS_REFCURSOR
)
as
v_call_log_id INSPECTION_CALL_LOG.call_log_id%type;
v_call_log_ref_no INSPECTION_CALL_LOG.call_log_ref_no%type;
begin
open c_cur for
select call_log_id, call_log_ref_no
from INSPECTION_CALL_LOG
where modified_by = in_vendor_code;
loop
fetch c_cur into v_call_log_id, v_call_log_ref_no;
exit when c_cur%notfound;
end loop;
close c_cur; --really needed here???
end GET_INSPECTION_CALL_REF_NO;
A REF CURSOR is a PL/SQL data type whose value is the memory address
of a query work area on the database. In essence, a REF CURSOR is a
pointer or a handle to a result set on the database
Here you are using the ref-cursor to return the result hence no point in closing the cursor.
Its the duty of the caller of this proc to close the cursor.
You cannot do var_c_cur c_cur%ROWTYPE; on ref-cursor
You have to use table.column%type or DBMS_SQL package.
This procedure can be rewritten to ,
create or replace
procedure GET_INSPECTION_CALL_REF_NO (
in_vendor_code varchar2,
c_cur out SYS_REFCURSOR
)
as
begin
open c_cur for
select call_log_id, call_log_ref_no
from INSPECTION_CALL_LOG
where modified_by = in_vendor_code;
end GET_INSPECTION_CALL_REF_NO;
Closing the Ref-Cursor;
DECLARE
v_cur SYS_REFCURSOR;
v_call_log_id INSPECTION_CALL_LOG.call_log_id%type;
v_call_log_ref_no INSPECTION_CALL_LOG.call_log_ref_no%type;
BEGIN
GET_INSPECTION_CALL_REF_NO('some_code', v_cur);
loop
fetch c_cur into v_call_log_id, v_call_log_ref_no;
exit when c_cur%notfound;
-- Use v_call_log_id, and v_call_log_ref_no...
end loop;
CLOSE v_cur; --Please note.
END;
/

Related

How to make a create a XML using DBMS_XMLGEN and then fetch and print using a single SYS_REFCURSOR?

I have a SYS_REFCURSOR opened for some query. I am making an XML for all the records in the SYS_REFCURSOR using DBMS_XMLGEN. Also I am trying to fetch the records into a record variable and then print OUTPUT. The issue is if I am creating only XML from the SYS_REFCURSOR or If I am only Fetching, it is working just fine. But if I am trying to do both, It is throwing a fetch out of sequence error.
Please help. Below is my Sample Code.
I have declared p_individual as a record type.
set serveroutput on;
declare
l_ctx dbms_xmlgen.ctxhandle;
l_xmltype clob;
l_xml xmltype;
L_REFCURSOR SYS_REFCURSOR;
begin
OPEN L_REFCURSOR FOR select * from some_table;
//Creating XML
L_CTX := dbms_xmlgen.newcontext(L_REFCURSOR);
dbms_xmlgen.setrowsettag(L_CTX, 'root');
dbms_xmlgen.setrowtag(L_CTX, 'data');
L_XML := dbms_xmlgen.getXmlType(L_CTX) ;
dbms_xmlgen.closeContext(L_CTX);
dbms_output.put_line(L_XML.getclobval());
//Fetching the same cursor to print output
loop
fetch L_REFCURSOR into p_individual;
exit when L_REFCURSOR%NOTFOUND;
dbms_output.put_line(p_individual.id);
end loop;
close L_REFCURSOR;
end;
/
Do your conventional fetch first then use DBMS_XMLGEN.RESTARTQUERY to output as XML.
Based on your example, something like this...
DECLARE
l_xmltype XMLTYPE;
l_refcursor SYS_REFCURSOR;
l_ctxhandle DBMS_XMLGEN.ctxhandle;
l_record v$version%ROWTYPE;
BEGIN
OPEN l_refcursor FOR SELECT * FROM v$version;
-- output conventional
LOOP
FETCH l_refcursor INTO l_record;
EXIT WHEN l_refcursor%NOTFOUND;
DBMS_OUTPUT.put_line (l_record.banner);
END LOOP;
-- output as xml
l_ctxhandle := DBMS_XMLGEN.newcontext (l_refcursor);
-- set some format stuff
DBMS_XMLGEN.setrowsettag (l_ctxhandle, 'root');
DBMS_XMLGEN.setrowtag (l_ctxhandle, 'data');
-- cursor already fetched so restart it
DBMS_XMLGEN.restartquery (l_ctxhandle);
l_xmltype := DBMS_XMLGEN.getxmltype (l_ctxhandle);
DBMS_XMLGEN.closecontext (l_ctxhandle);
IF l_refcursor%ISOPEN THEN
CLOSE l_refcursor;
END IF;
DBMS_OUTPUT.put_line (l_xmltype.getclobval);
END;
/

Does Oracle support non-scalar cursor parameter?

This is a question about Oracle PL/SQL.
I have a procedure in which the exact WHERE clause is not known until the run time:
DECLARE
CURSOR my_cursor is
SELECT ...
FROM ...
WHERE terms in (
(SELECT future_term2 FROM term_table), -- whether this element should be included is conditional
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
);
BEGIN
(the processing)
END;
/
What the (SELECT ... FROM term_table) query returns is a 4-character string.
For a solution to this, I am thinking of using a parameterized cursor:
DECLARE
target_terms SOME_DATATYPE;
CURSOR my_cursor (pi_terms IN SOME_DATATYPE) IS
SELECT ...
FROM ...
WHERE terms in my_cursor.pi_terms;
BEGIN
target_terms := CASE term_digit
WHEN '2' THEN (
(SELECT future_term2 FROM term_table),
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
) ELSE (
(SELECT future_term1 FROM term_table),
(SELECT present_term FROM term_table)
)
END;
FOR my_record IN my_cursor (target_terms) LOOP
(the processing)
END LOOP;
END;
/
The problem is what the datatype for SOME_DATATYPE should be is not known to me, nor is it known whether Oracle supports such a cursor parameter at all. If supported, is the way shown above to fabricate the value for target_terms correct? If not, how?
Hope someone who know can advise. And thanks a lot for the help.
You can certainly pass a parameter to a cursor, just like you can to a function - but only IN parameters. However, PL/SQL is a strongly typed language, so the datatype must be specified at the time of compilation.
It looks to me like what you will need to do is construct the query dynamically and then use
OPEN cursor FOR l_query;
where l_query is the constructed string. This should give you a feel for what you can do:
CREATE OR REPLACE PACKAGE return_id_sal
AUTHID DEFINER
IS
TYPE employee_rt IS RECORD
(
employee_id employees.employee_id%TYPE,
salary employees.salary%TYPE
);
FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
RETURN SYS_REFCURSOR;
END return_id_sal;
/
CREATE OR REPLACE PACKAGE BODY return_id_sal
IS
FUNCTION allrows_by (append_to_from_in IN VARCHAR2 DEFAULT NULL)
RETURN SYS_REFCURSOR
IS
l_return SYS_REFCURSOR;
BEGIN
OPEN l_return FOR
'SELECT employee_id, salary FROM employees ' || append_to_from_in;
RETURN l_return;
END allrows_by;
END return_id_sal;
/
DECLARE
l_cursor SYS_REFCURSOR;
l_row return_id_sal.employee_rt;
BEGIN
l_cursor := return_id_sal.allrows_by ('WHERE department_id = 10');
LOOP
FETCH l_cursor INTO l_row;
EXIT WHEN l_cursor%NOTFOUND;
END LOOP;
END;
/
You will need to take precautions against SQL injection with this sort of code. Certainly a user should never be able to pass SQL text directly to such a function!
You can use also some built-in VARRAY SQL types like SYS.ODCIVARCHAR2LIST or create your own :
CREATE OR REPLACE NONEDITIONABLE TYPE VARCHARLIST
AS VARRAY(32767) OF VARCHAR2(4000);
Then you can use it with SELECT COLUMN_VALUE FROM TABLE(COLLECTION) statement in your cursor:
DECLARE
l_terms SYS.ODCIVARCHAR2LIS; --or VARCHARLIST
CURSOR my_cursor (p_terms IN SYS.ODCIVARCHAR2LIS) IS
SELECT your_column
FROM your_table
WHERE terms in (select COLUMN_VALUE from table (p_terms));
BEGIN
select term
bulk collect into l_terms
from (
select 'term1' term from dual
union all
select 'term2' term from dual
);
FOR my_record IN my_cursor (l_terms) LOOP
--process data from your cursor...
END LOOP;
END;

returning value to cursor plsql

Hi I have a procedure which return a recordset using cursor in output what am trying to do is i use cursor to get the data and same have to return it to output cursor .I can do like below
PROCEDURE test(value_one IN someTabel.somecolumn%TYPE,
valu_two IN someTabel.somecolumn%TYPE,
Outputcursor OUT SYS_REFCURSOR) IS
mydeclaration goes here
output_value_one sometable.somecolumn%Type;
---
cursor test_select is
select statement
begin
for val in test_select loop
fetch test_select into output_value_one; -- I want my cursor outputcursor to be return instead of output_value_one
end loop;
end;
You send the deptno the cursor return the query where you can handle from frontend.
create or replace Procedure Transproc(p_deptno IN emp.deptno%TYPE,
Outputcursor Out Sys_Refcursor,
p_recordset Out Varchar)
Is
Vquery varchar2(200);
Begin
Vquery:='select * from emp where deptno='|| p_deptno ||'';
Open Outputcursor For Vquery;
OMessage:='Success';
Exception
When others then
OMessage:='Fail';
End;
OR try below one as per your requriement.
CREATE OR REPLACE PROCEDURE get_emp_rs (p_deptno IN emp.deptno%TYPE,
p_recordset OUT SYS_REFCURSOR) AS
BEGIN
OPEN p_recordset FOR
SELECT ename,
empno,
deptno
FROM emp
WHERE deptno = p_deptno
ORDER BY ename;
END GetEmpRS;
/
To test :-
SET SERVEROUTPUT ON SIZE 1000000
DECLARE
l_cursor SYS_REFCURSOR;
l_ename emp.ename%TYPE;
l_empno emp.empno%TYPE;
l_deptno emp.deptno%TYPE;
BEGIN
get_emp_rs (p_deptno => 30,
p_recordset => l_cursor);
LOOP
FETCH l_cursor
INTO l_ename, l_empno, l_deptno;
EXIT WHEN l_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(l_ename || ' | ' || l_empno || ' | ' || l_deptno);
END LOOP;
CLOSE l_cursor;
End;
/

PLS-00103: Encountered the symbol "IF" when expecting one of the following

Below is my function in Oracle:
create or replace
FUNCTION CALCULATEINT
( DebtNo IN VARCHAR2
, JFlag IN VARCHAR2
, FloatingInterestRate IN NUMBER
) RETURN NUMBER IS
AccInt NUMERIC(17,8):=0;
BEGIN
DECLARE
PrincipalDue NUMERIC(11,2);
InterestDue NUMERIC(17,8);
IF (JFlag IN ('B', 'C', 'Y')) THEN
BEGIN
SELECT
DEF_JUDG_PRINC_DUE ,
DEF_JUDG_PRINC_RATE
bulk collect into PrincipalDue ,InterestDue
FROM
DANT
WHERE
AND DE_NO = DebtNo ;
END;
END IF;
RETURN AccInt;
END;
I am getting errors below:
1.PLS-00103: Encountered the symbol "IF" when expecting one of the following: begin function pragma procedure subtype type current cursor delete exists prior The symbol "begin" was substituted for "IF" to continue.
2.PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge
create or replace
FUNCTION CALCULATEINT
( DebtNo IN VARCHAR2
, JFlag IN VARCHAR2
, FloatingInterestRate IN NUMBER
) RETURN NUMBER IS
AccInt NUMERIC(17,8):=0;
BEGIN
DECLARE
PrincipalDue NUMERIC(11,2);
InterestDue NUMERIC(17,8);
BEGIN
IF (JFlag IN ('B', 'C', 'Y')) THEN
SELECT
DEF_JUDG_PRINC_DUE ,
DEF_JUDG_PRINC_RATE
bulk collect into PrincipalDue ,InterestDue
FROM
DANT
WHERE
AND DE_NO = DebtNo ;
END IF;
END;
RETURN AccInt;
END;
Move the if statement from before the anonymous begin to after the begin.
I am certain that is the error.
Correspondingly you also need to move the end statement after the end if block.
Hope this helps.

Can I pass an explicit cursor to a function/procedure for use in FOR loop?

I have a procedure that performs some calculations on all records returned by a cursor. It looks a bit like this:
PROCEDURE do_calc(id table.id_column%TYPE)
IS
CURSOR c IS
SELECT col1, col2, col3
FROM table
WHERE ...;
BEGIN
FOR r IN c LOOP
-- do some complicated calculations using r.col1, r.col2, r.col3 etc.
END LOOP;
END;
Now I have the case where I need to perform the exact same calculation on a different set of records that come from a different table. However, these have the same "shape" as in the above in example.
Is it possible to write a procedure that looks like this:
PROCEDURE do_calc2(c some_cursor_type)
IS
BEGIN
FOR r IN c LOOP
-- do the calc, knowing we have r.col1, r.col2, r.col3, etc.
END LOOP;
END;
I know about SYS_REFCURSOR, but I was wondering if it was possible to use the much more convenient FOR ... LOOP syntax and implicit record type.
Create a package.
Declare your cursor as package variable.
Use %rowtype to set function parameter type.
create or replace package test is
cursor c is select 1 as one, 2 as two from dual;
procedure test1;
function test2(test_record c%ROWTYPE) return number;
end test;
create or replace package body test is
procedure test1 is
begin
for r in c loop
dbms_output.put_line(test2(r));
end loop;
end;
function test2(test_record c%ROWTYPE) return number is
l_summ number;
begin
l_summ := test_record.one + test_record.two;
return l_summ;
end;
end test;
I had a similar problem, where I had two cursors that needed to be processed the same way, so this is how I figured it out.
DECLARE
--Define our own rowType
TYPE employeeRowType IS RECORD (
f_name VARCHAR2(30),
l_name VARCHAR2(30));
--Define our ref cursor type
--If we didn't need our own rowType, we could have this: RETURN employees%ROWTYPE
TYPE empcurtyp IS REF CURSOR RETURN employeeRowType;
--Processes the cursors
PROCEDURE process_emp_cv (emp_cv IN empcurtyp) IS
person employeeRowType;
BEGIN
LOOP
FETCH emp_cv INTO person;
EXIT WHEN emp_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('Name = ' || person.f_name ||
' ' || person.l_name);
END LOOP;
END;
--Defines the cursors
PROCEDURE mainProcedure IS
emp empcurtyp;
BEGIN
OPEN emp FOR SELECT first_name, last_name FROM employees WHERE salary > 50000;
process_emp_cv(emp);
CLOSE emp;
OPEN emp FOR SELECT first_name, last_name FROM kuren WHERE first_name LIKE 'J%';
process_emp_cv(emp);
CLOSE emp;
END;
BEGIN
mainProcedure;
END;
/
You can also use this if you want to bulk collect your cursors. You just need to change your helper procedure process_emp_cv; the rest can stay the same.
Using BULK COLLECT
--Processes the cursors
PROCEDURE process_emp_cv (emp_cv IN empcurtyp) IS
TYPE t_employeeRowTable IS TABLE OF employeeRowType;
employeeTable t_employeeRowTable;
BEGIN
LOOP
FETCH emp_cv BULK COLLECT INTO employeeTable LIMIT 50;
FOR indx IN 1 .. employeeTable.Count
LOOP
DBMS_OUTPUT.PUT_LINE('Name = ' || employeeTable(indx).f_name ||
' ' || employeeTable(indx).l_name);
END LOOP;
EXIT WHEN emp_cv%NOTFOUND;
END LOOP;
END;
Try this one, Usong ref cursor.
declare
type c is ref cursor;
c2 c;
type rec is record(
id number,
name varchar(20)
);
r rec;
procedure p1(c1 in out c,r1 in out rec)is begin
loop
fetch c1 into r1;
exit when c1%notfound;
dbms_output.put_line(r1.id || ' ' ||r1.name);
end loop;
end;
begin
open c2 for select id, name from student;
p1(c2,r);
end;
Yes you can use Cursor explicitly into procedure and function,for that cursor need to declare into package as variable

Resources