コードに空白を追加すると、何をしているかが明確になります。
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;
/
そこには多くの矛盾があります。
- 1 と 2 では、カーソルを開き、開いているカーソルがあるかどうかを確認しています。カーソルが開かなかった場合はエラーが発生するため、この手順は無視してかまいません。
- 5 と 6 では、新しいレコードをフェッチできない場合は終了し、レコードがあるかどうかを確認します。これは矛盾しているため、ステージ 6 は(ほぼ)常に true と評価されます。
- 7 と 8 ではループし、レコードがなくなると終了します。実際にレコードがあることを(2回)確認したので、このループを終了することはありません。
カーソルでこれを行うことを主張する場合は、ほとんどのコードを削除することができ、正常に動作するはずです:
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;
/
いつものように、ループ、カーソル、または PL/SQL を使用しない単一の UPDATE ステートメントは、はるかに効果的です。