Cursor error in SQL - sql

I'm trying to implement a cursor, & after solving many errors I've finally come to a point where it runs, but it goes into infinite loop...
I've put the image of table below as image.
Aim of cursor: to calculate bowling average & store in 'bowling_avg' column.
here's the cursor code :
DECLARE
CURSOR BOWL_AVG IS SELECT SID,MATCHES,BOWLING_AVG,WICKETS
FROM BOWLING_STATS ;
NSID BOWLING_STATS.SID%TYPE;
NMATCHES BOWLING_STATS.MATCHES%TYPE;
NBOWLING_AVG BOWLING_STATS.BOWLING_AVG%TYPE;
NWICKETS BOWLING_STATS.WICKETS%TYPE;
BEGIN
OPEN BOWL_AVG;
IF BOWL_AVG%ISOPEN THEN
LOOP
FETCH BOWL_AVG INTO NSID,NMATCHES,NBOWLING_AVG,NWICKETS;
EXIT WHEN BOWL_AVG%NOTFOUND;
IF BOWL_AVG%FOUND THEN
LOOP
UPDATE BOWLING_STATS SET BOWLING_AVG=NWICKETS/NMATCHES WHERE SID=NSID ;
EXIT WHEN BOWL_AVG%NOTFOUND;
END LOOP;
END IF;
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE('UNABLE TO OPEN CURSOR');
END IF;
CLOSE BOWL_AVG;
END;
I'm running this in oracle database 10g.
I ask for assistance in finding the error.
Thanks in advance.

Adding whitespace to your code makes it clearer what you're doing:
declare
cursor bowl_avg is
select sid, matches, bowling_avg, wickets
from bowling_stats;
nsid bowling_stats.sid%type;
nmatches bowling_stats.matches%type;
nbowling_avg bowling_stats.bowling_avg%type;
nwickets bowling_stats.wickets%type;
begin
-- 1. Open Cursor
open bowl_avg;
-- 2. Check if Cursor is open
if bowl_avg%isopen then
-- 3. Loop
loop
-- 4. Get record
fetch bowl_avg into nsid, nmatches, nbowling_avg, nwickets;
-- 5. Exit if no records left
exit when bowl_avg%notfound;
-- 6. If there is a record
if bowl_avg%found then
-- 7. Loop
loop
update bowling_stats
set bowling_avg = nwickets / nmatches
where sid = nsid;
-- 8. Exit if there is no record.
exit when bowl_avg%notfound;
end loop;
end if;
end loop;
else
dbms_output.put_line('unable to open cursor');
end if;
close bowl_avg;
end;
/
There are a number of contradictions in there.
In 1 and 2 you're opening a cursor and then checking if there is an open cursor. A error will be raised if the cursor didn't open so you can ignore this step.
In 5 and 6 you exit if you can't fetch a new record then check if you have a record. This is a contradiction so stage 6 will (almost) always evaluate to true.
in 7 and 8 you loop, exiting when you don't have a record. As you've just checked (twice) that you do in fact have a record you'll never exit this loop.
If you insist on doing this with cursors then you can remove most of your code and it should work fine:
declare
cursor bowl_avg is
select sid, matches, bowling_avg, wickets
from bowling_stats;
nsid bowling_stats.sid%type;
nmatches bowling_stats.matches%type;
nbowling_avg bowling_stats.bowling_avg%type;
nwickets bowling_stats.wickets%type;
begin
-- 1. Open Cursor
open bowl_avg;
-- 2. Loop
loop
-- 3. Get record
fetch bowl_avg into nsid, nmatches, nbowling_avg, nwickets;
-- 4. Exit loop if there is no record.
exit when bowl_avg%notfound;
-- 5. Perform UPDATE statement.
update bowling_stats
set bowling_avg = nwickets / nmatches
where sid = nsid;
end loop;
close bowl_avg;
end;
/
As always a single UPDATE statement without using loops, cursors or PL/SQL will be significantly more effective.

Related

Printing odd and even numbers in pl/sql

I'm new to programming; I want to print odd and even number inside a loop the problem that the if does not work and it will always print else no matter the number you have entered:
DECLARE
x NUMBER:=:x;
r NUMBER:=:r;
BEGIN
LOOP
if x/2=0 AND r/2!=0 then
dbms_output.put_line('Even number x='||x);
dbms_output.put_line('Odd number r='||r);
x:= x+2;
r:= r+2;
else
dbms_output.put_line('Odd number x='||x);
dbms_output.put_line('Even number r='||r);
x:=x+2;
r:=r+2;
exit when x>20 and r>20;
end if;
end loop;
end;
Instead of x/2=0 you need to use mod(x,2)=0 to check whether it's even number or not. I have separated the condition for x and r. Check out below code:
DECLARE
x NUMBER:=2;
r NUMBER:=3;
BEGIN
LOOP
if mod(x,2)=0 then
dbms_output.put_line('Even number x='||x);
else
dbms_output.put_line('Odd number x='||x);
end if;
if mod(r,2)=0 then
dbms_output.put_line('Even number r='||r);
else
dbms_output.put_line('Odd number r='||r);
end if;
x:= x+2;
r:= r+2;
exit when x>20 and r>20;
end loop;
end;

Running update from SQL Developer with different results as from package

I tried to run an update within an cursor ('cur_palettenkosten') operation in an plsql procedure.
I narrowed down, that the enclosing cursor has data and that the update does not affect any rows (output sql%rowcount)
PROCEDURE p_ref_lschein_rueckstellungen AS
for cur_palettenkosten in (
select land, spediteur_nr, plz_von, plz_bis, preis, gueltig_von, geultig_bis, gzp.behaelter_nr
from spediteur_fahrtkosten sp,gutschrift_zuord_pal gzp
where sp.behaelter_nr = 1
)
LOOP
UPDATE lschein_rueckstellungen
SET preis = cur_palettenkosten.preis
WHERE to_number(sped_nr) = to_number(cur_palettenkosten.spediteur_nr)
AND to_number(lhm_typ) = to_number(cur_palettenkosten.behaelter_nr)
AND to_char(kst) = to_char(cur_palettenkosten.land);
dbms_output.put_line (cur_palettenkosten.spediteur_nr || ' '||cur_palettenkosten.behaelter_nr|| ' '|| cur_palettenkosten.land || sql%rowcount);
END LOOP;
COMMIT;
END p_ref_lschein_rueckstellungen;
Running the script from the editor this way:
BEGIN
p_ref_lschein_rueckstellungen;
END
does have any effect on the table 'lschein_rueckstellungen' which I wanted to update.
Running it from the same editor window like this:
BEGIN
<procedure content copied here>
END
updates the data as desired.
Are the any ideas, what I did wrong?
Could it be that you open a new session per tab? Then of course you have to commit the update!

how to exit the procedure if condition met in a loop PL SQL

Let's say I have a for loop
for i in array.first .. array.last loop
boolean := c(i) > d(i);
if boolean --is true then
exit the loop immediately and also exit the entire procedure
else if the boolean is never true til the end of the loop, exit the loop
and keep running other scripts in this procedure.
I know the 'EXIT' keyword needs to be inside of the loop in order to exit the loop if condition is met. And 'RETURN' needs to be outside of the loop, to exit the procedure.
But if I put 'RETURN' outside of the loop, then I think no matter what the result is from the loop, it will exit the entire procedure when the loop ends?
If you want to be didactic about it, you should use an EXIT to get out of the loop, then use a RETURN to exit from the procedure (duplicating any necessary tests), thus following the structured programming rule that "A procedure should only have a single entrance and a single exit". In practice 99.999% of programmers would just code the RETURN inside the body of the loop because A) it's clearer as to what's going on (you're not just getting out of the loop, you're returning from the procedure), and B) it's shorter. Do as you will. Best of luck.
define an exception, and when ever you want to exit raise and handle the exception as
create procedure exit_loop_example as
exit_loop_exception exception;
begin
/* previous code block */
begin
for i in 1..20 loop
raise exit_loop_exception;
end loop;
exception when
exit_loop_exception then
/* handle exit loop*/
null;
end;
/* next code block */
end;
The simple loop. It’s called simple for a reason: it starts simply with the LOOP keyword and ends with the END LOOP statement. The loop will terminate if you execute an EXIT, EXIT WHEN, or RETURN within the body of the loop (or if an exception is raised).
See Oracle Magazine
Following through with Tamás Kecskeméti's link, the only recommended way is to use a while loop with the desired condition specified in the beginning itself.
Below is and excerpt from the above link :
Code Listing 5: A WHILE loop with one exit
PROCEDURE display_multiple_years (
start_year_in IN PLS_INTEGER
, end_year_in IN PLS_INTEGER)
IS
l_current_year PLS_INTEGER := start_year_in;
BEGIN
WHILE ( l_current_year <= end_year_in
AND total_sales_for_year (l_current_year) > 0)
LOOP
display_total_sales_for_year (l_current_year);
l_current_year := l_current_year + 1;
END LOOP;
END display_multiple_years;

Simple PL/SQL Code will not run. I cannot find the error

I've been tweaking and trying to debug this PL/SQL code for like 4 hours now. I have also tried to search on here but it is so specific that I really need help. Here is my code, When I try to run it, the two prompt questions pop up. After I answer the second one, oracle just stops running.
---- File PLh20.sql
-- Author: <<< NAME >>>
-------------------------------------------------------------------
SET SERVEROUTPUT ON
SET VERIFY OFF
------------------------------------
ACCEPT rateDecrement NUMBER PROMPT 'Enter the rate decrement: '
ACCEPT allowedMinRate NUMBER PROMPT 'Enter the allowed min. rate: '
DECLARE
sr boats%ROWTYPE;
CURSOR sCursor IS
SELECT B.bid, B.bname, B.color, B.rate, B.length, B.logKeeper
FROM Boats B
WHERE B.bid NOT IN (SELECT bid FROM Reservations);
BEGIN
OPEN sCursor;
LOOP
-- Fetch the qualifying rows one by one
FETCH sCursor INTO sr;
EXIT WHEN sCursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE ('+++++ old rate: '||sr.rate||' '
||sr.rate||);
sr.rate := sr.rate - &rateDecrement;
-- A nested block
DECLARE
belowAllowedMin EXCEPTION;
BEGIN
IF sr.rate < &allowedMinRate
THEN RAISE belowAllowedMin;
ELSE UPDATE Boats
SET rate = sr.rate
WHERE Boats.bid = sr.bid;
-- Print the boat new record
DBMS_OUTPUT.PUT_LINE ('+++++ new row: '||sr.bid||' '
||sr.rate||);
END IF;
EXCEPTION
WHEN belowAllowedMin THEN
DBMS_OUTPUT.PUT_LINE('+++++ Update rejected: '||
'The new rate would have been: '|| sr.rate);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('+++++ update rejected: ' ||
SQLCODE||'...'||SQLERRM);
END;
-- end of the nested block
END LOOP;
COMMIT;
CLOSE sCursor;
END;
SELECT S.sid, S.rating
FROM sailors S, reservations R, boats B
WHERE S.sid = R.sid AND
R.bid = B.bid;
UNDEFINE rateDecrement
UNDEFINE allowedMinRate
DBMS_OUTPUT.PUT_LINE ('+++++ new row: '||sr.bid||' '||sr.rate||);
DBMS_OUTPUT.PUT_LINE ('+++++ old rate: '||sr.rate||' '||sr.rate||);
Looks like these 2 are the problem. There should not be '||' at the end.

Skip a loop iteration with Firebird 2.5

I need to skip a While...Do loop iteration inside a stored procedure like this
While (v_counter <= :v_total) do begin
If (<condition>) then continue;
...
end
However CONTINUE won't be available until Firebird 3.0. Is there a work a round for this?
If you want to skip an iteration through a loop without CONTINUE, then just use the inverse of the continue-condition for the rest of the block:
While (v_counter <= :v_total) do begin
If (NOT <condition>) then
BEGIN
...
END
end