PL/SQL cursor in the while - oracle

This is what I am trying to achieve:
define a cursor
which return me list of rows
use the rows to do some modify tables
repeat 2 and 3 until cursor returns no result.
this is what I have done so far
DECLARE
CURSOR c1 IS
[My SQL HERE];
BEGIN
FOR r1 in c1 LOOP
[modify tables]
END;
question is where do I put the while? I would do it before the for loop. but whats the syntax in pl/sql to describe c1 has result?
Note after c1 fullly finish I need to rerun the cursor code. Make another iteration . Because the result will be different. That's why I needed the while

The answer is combine 'while' with 'for '. Obviously only if you want to run the cursor multiple times, because you are update/alter something inside the cursor, and the condition changed, thus you need to re-run the cursor again.
here is the basic structure:
DECLARE
CURSOR c1 IS
[My SQL HERE];
BEGIN
WHILE CONDITION LOOP
FOR r1 in c1 LOOP
[modify tables]
END LOOP; -- for loop
[Check The Condition]
END LOOP; -- while loop
END;
e.g
DECLARE
counts NUMBER := -1;
CURSOR c1 IS
[Statement Here];
BEGIN
WHILE count != 0 LOOP
FOR r1 in c1 LOOP
[modify tables]
END LOOP; -- for loop
SELECT count(*) ...... INTO counts
END LOOP; -- while loop
END;

First of all I would recommend to think about conventional update without cursors because the best part of them is slower. I also don't understand what for you would like to replace FOR by WHILE. They work nearly similar. At any case the best source of advice is the documentation from Oracle http://docs.oracle.com/cd/B14117_01/appdev.101/b10807/13_elems020.htm

You don't need a separate WHILE. The FOR ... LOOP will iterate over the rows returned by the cursor:
FOR r1 IN c1 LOOP
-- r1 is the current row from the cursor
END LOOP;
There are two ways to handle a cursor, so perhaps that's caused confusion; the alternative is something like:
OPEN c1;
LOOP
FETCH c1 INTO r1; -- where r1 is declared
EXIT WHEN c1%NOTFOUND;
-- do something with r1
END LOOP;
CLOSE c1;
Personally I usually find the FOR ... LOOP syntax simpler.

Related

I want to call this procedure and pass the variables that are fetched in the cursor

I want to pass these variables but getting error
my code:
begin
open CUR_SCENARIO_1;
fetch CUR_SCENARIO_1 bulk collect
into v_id, v_state, v_toc, v_sub, v_resp, v_pp_status limit 10;
FOR x in v_id.count()
SP_Create_original_record(v_id, v_state, v_toc, v_sub, v_resp, v_pps);
END LOOP;
end;
you need a Loop keyword and a The starting and the ending value of the Loop.
FOR x in 1 .. v_id.count() loop
SP_Create_original_record(v_id, v_state, v_toc, v_sub, v_resp, v_pps);
END LOOP;
You also need to go a step further; nest the FOR loop in a "fetch loop". Without doing so you will process the first 10 rows from you cursor and the exit. If the cursor returns more, the additional will not be processed.
loop -- fetch loop
fetch CUR_SCENARIO_1 bulk collect into v_id, ... limit 10;
for x in 1..v_id.count()
loop
...
end loop ;
exit when v_id.count() < 10;
end loop; -- fetch loop

How to execute Stored Procedure inside Stored Procedure with Cursor in Oracle

I am new in Oracle,
In SQL Server I can easily execute a stored procedure inside storedprocedure
even using a cursor.
now I can't figure it out in Oracle here is my code below.
CREATE OR REPLACE PROCEDURE ZSP_INSMASTERDATASTM
AS
l_total INTEGER := 10000;
CURSOR c1
IS
SELECT DISTINCT PRODFROMTO FROM DJ_P9945LINKS;
l_PRODFROMTO c1%ROWTYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO l_PRODFROMTO;
EXIT WHEN c1%NOTFOUND;
EXECUTE ZSP_GETMASTERDATASTM (l_PRODFROMTO);
EXIT WHEN l_total <= 0;
END LOOP;
CLOSE c1;
END ZSP_INSMASTERDATASTM;
i got error on execute ZSP_GETMASTERDATASTM (l_PRODFROMTO);
Just remove EXECUTE. However, note that your loop will NEVER exit because L_TOTAL is never going to be less than zero - you should fix that.
Otherwise, consider using cursor FOR loop as it is simpler to maintain - you don't have to declare a cursor variable, open cursor, fetch, take care about exiting the loop, close the cursor. Have a look at this example:
CREATE OR REPLACE PROCEDURE zsp_insmasterdatastm
AS
BEGIN
FOR cur_r IN (SELECT DISTINCT prodfromto FROM dj_p9945links)
LOOP
zsp_getmasterdatastm (cur_r.prodfromto);
END LOOP;
END;
Quite simpler, isn't it?

PL/SQL - Pre actions if cursor is found

I have a simple cursor like this:
CURSOR emp_cur
IS
SELECT *
FROM employee
WHERE age > 20;
In my procedure, I want to do some pre-actions only if there are employee in cursor. After that pre-action, I process all rows.
I need this because only if exists employee in that cursor i need to cleanup some tables, otherwise i should "RETURN".
so the code could be:
OPEN emp_cur;
/* Here i need to do pre-action only if emp_cur has rows*/
IF /* has rows*/
THEN
/* do some actions*/
END IF;
LOOP
FETCH emp_cur INTO emp_rec;
EXIT WHEN emp_cur%NOTFOUND;
END LOOP;
CLOSE emp_cur;
For now, i have a "dirty" solution where i open cursor:
First to check if there are rows
Do pre-action and close
Open/fetch again to process rows, and close again
First to check if there are rows
You cannot know about the rows until you FETCH.
From documentation link ,
After a cursor or cursor variable is opened but before the first
fetch, %FOUND returns NULL. After any fetches, it returns TRUE if the
last fetch returned a row, or FALSE if the last fetch did not return a
row.
Once you have fetched the rows, then before processing the rows, you could use %FOUND.
For example,
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
IF c1%FOUND THEN -- fetch succeeded
-- Do something
ELSE -- fetch failed, so exit loop
EXIT;
END IF;
END LOOP;
Thinking a little i've wrote this procedure that avoid an IF inside the loop. I know, is a little "strange" but is the only thing i've think works:
OPEN emp_cur;
FECTH emp_cur INTO emp_rec;
IF emp_cur%FOUND
THEN
-- pre actions
END IF;
LOOP
EXIT WHEN emp_cur%NOTFOUND;
-- do something in the loop
FECTH emp_cur INTO emp_rec; -- First fetch was done before the if
END LOOP;
CLOSE emp_cur;

Need an alternative solution to this oracle query?Without using (flag) variable

Question:
Create a Doctor table (Docname, Qualification, Specialization, Working_shift).
Use parameterized cursor to check the availability of doctors given the specialization
and working shift of the day to serve the patients
I am just learning databases so if the question may seem trivial i apologize for that.
Getting the desired output on inputting the values but i need an alternative way to solve the question without using flag variable (so that i could get the exception)...if i don't use the flag it prints the exception as well as the docname and qualification
I am using oracle(cursor in a normal pl/sql block) to execute this query.
Solution:
--table creation
create table doctor
(
docname varchar2(20),
qualification varchar2(20),
specialization varchar2(20),
shift varchar2(20)
)
my solution
declare
cursor c1 (specialization varchar2,shift varchar2) is select docname,qualification from doctor
where specialization='&sp' and shift='&shift'
sp doctor.specialization%type;
shift doctor.shift%type;
flag number(10);
begin
flag:=0;
for r1 in c1(sp,shift)
loop
if c1%found then
flag:=1;
dbms_output.put_line('Doctor is available');
dbms_output.put_line('Docname: '||r1.docname);
dbms_output.put_line('qualification: '||r1.qualification);
else
flag:=0;
end if;
end loop;
if flag=0 then
dbms_output.put_line('Invalid specialization/shift');
end if;
end;
Try given below code
declare
cursor c1 (specialization varchar2,shift varchar2)
is
select docname,qualification
from doctor
where specialization='&sp'
and shift='&shift'
sp doctor.specialization%type;
shift doctor.shift%type;
flag number(10);
begin
flag:=0;
for r1 in c1(sp,shift)
loop
if c1%found then
flag:=1;
dbms_output.put_line('Doctor is available');
dbms_output.put_line('Docname: '||r1.docname);
dbms_output.put_line('qualification: '||r1.qualification);
else
raise;
end if;
end loop;
exception
when others then
dbms_output.put_line('Invalid specialization/shift');
end;
You don't need to reset the flag within the loop, you already initialised it to 0 at the start of the procedure.
You dont need to check c1%found because you're inside the loop; by definition a record was found, otherwise it wouldn't go into your loop code.
Your cursor should use the variables provided, not the SQL*Plus substitution variables, e.g.:
cursor c1 (specialization varchar2,shift varchar2) is
select docname,qualification
from doctor
where doctor.specialization=c1.specialization
and doctor.shift=c1.shift;
If you don't want to have to use all those aliases, you can use a naming convention to distinguish between the different identifiers (shift vs shift), e.g.:
cursor c1 (i_specialization varchar2, i_shift varchar2) is
select docname,qualification
from doctor
where specialization=i_specialization
and shift=i_shift;
Note also, you missed a semicolon at the end of the query.
Finally:
If you change your loop as follows, it should work fine:
for r1 in c1(&sp,&shift)
loop
flag:=1;
dbms_output.put_line('Doctor is available');
dbms_output.put_line('Docname: '||r1.docname);
dbms_output.put_line('qualification: '||r1.qualification);
end loop;
Now, your last bit of code:
if flag=0 then
dbms_output.put_line('Invalid specialization/shift');
end if;
will work fine - it will only execute if flag is still 0 (i.e. the query found no rows).
If you do not use parameters in "c1" cursor you do not need it...
DECLARE
CURSOR c1 IS
SELECT docname, qualification
FROM doctor
WHERE specialization = '&sp'
AND shift = '&shift';
TYPE c1_ntt IS TABLE OF c1%ROWTYPE;
l_c1 c1_ntt;
BEGIN
OPEN c1;
FETCH c1 BULK COLLECT INTO l_c1;
CLOSE c1;
IF l_c1.COUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20000, 'Invalid specialization/shift');
END IF;
FOR indx IN l_c1.FIRST..l_c1.LAST LOOP
DBMS_OUTPUT.PUT_LINE('Doctor is available');
DBMS_OUTPUT.PUT_LINE('Docname: ' || l_c1(indx).docname);
DBMS_OUTPUT.PUT_LINE('qualification: ' || l_c1(indx).qualification);
END LOOP;
END;

How do you fetch a cursor variable multiple times

I am passing a cursor as input parameter in a function.
Function (p_cur IN curType) ...
Loop (outer loop)
Loop (inner loop)
fetch p_cur into p_cur_rec;
exit when p_cur%notfound;
do some processing here...
end loop;
END Loop;
The inner loop fetches all the cursor records and i was able to go through each iteration to process the data.
However, when the outer loop fetches the 2nd record, the inner loop doesn't seem to fetch from the cursor again. It appears the cursor record is already fetched and can't be used again.
My question is how do you overcome this issue so that the cursor can be fetched again?
You can use the cursor to do your inner loop like this :
cursor c_1 as
select ...
from ..
where ...
rec c_1%rowtype;
begin
for rec in c_1 loop
...
end loop;
end;

Resources