Really need help regarding Ref Cursor. I have a Stored Procedure GET_PERSONROLES that have parameter type ref cursor. I just wanted to pupulate this ref cursor manually like inserting a row to the refcursor. Can I insert a row into a refcursor though a loop? Thank you in advance.
The procedure depends on this publicly declared type:
create or replace package types
as
type cursorTypePersonRole is ref cursor;
end;
Here is my pseudo-codeL
create or replace PROCEDURE GET_PERSONROLES
(
P_CURSOR IN OUT types.cursorTypePersonRole
) AS
REFCUR SYS_REFCURSOR;
TYPE REFTABLETYPE IS RECORD (
IS_MANAGER_LEVEL1 VARCHAR2(1),
IS_MANAGER_LEVEL2 VARCHAR2(1)
);
TYPE TABLETYPE IS TABLE OF REFTABLETYPE;
PERSONROLES_TABLETYPE TABLETYPE;
BEGIN
--calls another stored proc to populate REFCUR with data without problem
MY_STOREDPROC('12345', REFCUR);
LOOP
FETCH REFCUR BULK COLLECT INTO PERSONROLES_TABLETYPE;
EXIT WHEN PERSONROLES_TABLETYPE.COUNT = 0;
FOR indx IN 1 .. PERSONROLES_TABLETYPE.COUNT
LOOP
-- I'm able to query perfectly the values of IS_MANAGER_LEVEL1 and IS_MANAGER_LEVEL 2
-- I'm aware that the below codes are wrong
-- However this means I wanted to insert these values to a row of the cursor if possible
-- Do some logic to know what data will be assigned in the row.
if PERSONROLES_TABLETYPE(indx).IS_MANAGER_LEVEL1 = 'Y' then
P_CURSOR := <DO SOME LOGIC AND ASSIGN THE VALUE TO THE ROW>
end if;
if PERSONROLES_TABLETYPE(indx).IS_MANAGER_LEVEL2 = 'Y' then
P_CURSOR := <DO SOME LOGIC AND ASSIGN THE VALUE TO THE ROW>
end if;
END LOOP;
END LOOP;
CLOSE REFCUR;
END GET_PERSONROLES;
A ref cursor is not a variable: it is a pointer to a result set which is consumed by the act of reading it. The result set itself is immutable.
Immutability makes sense, because it reflects Oracle's emphasis on read consistency.
The simplest way to produce the output you appear to want is to create a SQL Type
open P_CURSOR for
select IS_MANAGER_LEVEL1,
IS_MANAGER_LEVEL2
from table ( PERSONROLES_TABLETYPE );
This will work in 12c; in earlier versions to use the table()
call like this you may need to declare REFTABLETYPE
and TABLETYPE
as SQL types( rather than in PL/SQL).
"Ok edited it now"
Alas your requirements are still not clear. You haven't given us the structure of the output ref cursor or shown what other processing you want to undertake.
However, given the title of your question, let's have a guess. So:
create or replace PROCEDURE GET_PERSONROLES ( P_CURSOR IN OUT types.cursorTypePersonRole) AS
REFCUR SYS_REFCURSOR;
TYPE REFTABLETYPE IS RECORD (IS_MANAGER_LEVEL1 VARCHAR2(1),
IS_MANAGER_LEVEL2 VARCHAR2(1));
TYPE TABLETYPE IS TABLE OF REFTABLETYPE;
PERSONROLES_TABLETYPE TABLETYPE;
personrole_rec PersonRole%rowtype;
type personrole_nt is table of PersonRole%rowtype;
personroles_recs personrole_nt := new personrole_nt() ;
BEGIN
MY_STOREDPROC('12345', REFCUR);
FETCH REFCUR BULK COLLECT INTO PERSONROLES_TABLETYPE;
FOR indx IN 1 .. PERSONROLES_TABLETYPE.COUNT LOOP
/* in the absence of requirements I'm just making some stuff up */
if PERSONROLES_TABLETYPE(indx).IS_MANAGER_LEVEL1 = 'Y' then
personrole_rec.whatever1 := 'something';
else
personrole_recc.whatever1 := null;
end if;
if PERSONROLES_TABLETYPE(indx).IS_MANAGER_LEVEL2 = 'Y' then
personrole_rec.whatever2 := 'something else';
else
personrole_recc.whatever2 := null;
end if;
if personrole_rec.whatever1 is not null
or personrole_rec.whatever2 is mot null then
personroles_recs.exend();
personroles_recs(personroles_recs.count()) := personroles_rec;
end if;
END LOOP;
CLOSE REFCUR;
open p_cursor for
select * from table ( personroles_recs );
END GET_PERSONROLES;
This code uses a second collection to store the desired output. Like your code it reads the populated collection and evaluates the attributes of each row. If a value which means the criteria it sets an attribute in a rowtype variable. If one or both attributes are set it populates a new row in a second collection. At the end of the procedure it opens the ref cursor using a table()
function call on the second collection.
Note that you do not need the nested loop: you're not using the LIMIT clause so your coder reads the entire cursor into the collection in one swoop.
The implemented rules may not be exactly what you want (because you haven't explained exactly what you want) but this should give you the general idea.
Note that, depending on exactly what processing is masked by <DO SOME LOGIC AND ASSIGN THE VALUE TO THE ROW>
, the simpler approach could still be feasible:
open P_CURSOR for
select case when IS_MANAGER_LEVEL1 = 'Y' then 'YES' else 'NO' end,
case when IS_MANAGER_LEVEL2 = 'Y' then 'YES' else 'NO' end
from table ( PERSONROLES_TABLETYPE );