これが私のカーソルです:
CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
手順の間、これらのレコードをロックするために、すぐにカーソルを開きます。
カーソルに2つ未満のレコードがある場合に、アプリケーションエラーを発生させたい。 C1%ROWCOUNTプロパティの使用は、これまでにフェッチされた数のみをカウントするため失敗します。
このユースケースに最適なパターンは何ですか?ダミーのMY_TABLE%ROWTYPE変数を作成し、カーソルをループしてそれらをフェッチしてカウントを維持する必要がありますか、それとももっと簡単な方法がありますか?これがその方法である場合、カーソル内のすべての行をフェッチすると暗黙的に閉じてロックが解除されますか、それともすべてをフェッチしたとしても明示的に閉じるまで開いたままになりますか?
この数を超える他のさまざまなタスクのために、カーソルが開いたままであることを確認する必要があります。
注意:質問を読み直しました。レコードが1つしかない場合は失敗したいのですが、すぐに新しい更新を投稿します。
ここから始めましょう。
Oracle®DatabasePL/ SQLユーザーズ・ガイドおよびリファレンス10gリリース2(10.2)から部品番号B14261-01リファレンス
カーソルを開くと、フェッチされるときではなく、すべての行がロックされます。トランザクションをコミットまたはロールバックすると、行のロックが解除されます。行がロックされなくなったため、コミット後にFORUPDATEカーソルからフェッチすることはできません。
したがって、レコードのロック解除について心配する必要はありません。
だからこれを試してみてください。
declare
CURSOR mytable_cur IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
TYPE mytable_tt IS TABLE OF mytable_cur %ROWTYPE
INDEX BY PLS_INTEGER;
l_my_table_recs mytable_tt;
l_totalcount NUMBER;
begin
OPEN mytable_cur ;
l_totalcount := 0;
LOOP
FETCH mytable_cur
BULK COLLECT INTO l_my_table_recs LIMIT 100;
l_totalcount := l_totalcount + NVL(l_my_table_recs.COUNT,0);
--this is the check for only 1 row..
EXIT WHEN l_totalcount < 2;
FOR indx IN 1 .. l_my_table_recs.COUNT
LOOP
--process each record.. via l_my_table_recs (indx)
END LOOP;
EXIT WHEN mytable_cur%NOTFOUND;
END LOOP;
CLOSE mytable_cur ;
end;
ALTERNATE ANSWER私はあなたが逆に答えて開始するのを読んで、1行以上ある場合は終了したいと思った..正確には1つではない..だからここにある私の前の答え。
1つのレコードのみをチェックする2つの簡単な方法。
オプション1-明示的なフェッチ
declare
CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
l_my_table_rec C1%rowtype;
l_my_table_rec2 C1%rowtype;
begin
open C1;
fetch c1 into l_my_table_rec;
if c1%NOTFOUND then
--no data found
end if;
fetch c1 into l_my_table_rec2;
if c1%FOUND THEN
--i have more then 1 row
end if;
close c1;
-- processing logic
end;
私はあなたがその考えを理解することを望みます。
オプション2-例外キャッチ
declare
CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;
l_my_table_rec C1%rowtype;
begin
begin
select *
from my_table
into l_my_table_rec
where salary < 50000
for update;
exception
when too_many_rows then
-- handle the exception where more than one row is returned
when no_data_found then
-- handle the exception where no rows are returned
when others then raise;
end;
-- processing logic
end;
さらに覚えておいてください:明示的なカーソルを使用すると、元のテーブルではなく、カーソルレコードから変数を%TYPEできます。
これは、クエリに結合がある場合に特に便利です。
また、テーブルの行を更新することもできます。
UPDATE table_name
SET set_clause
WHERE CURRENT OF cursor_name;
タイプステートメントですが、2行目を「フェッチ」していない場合にのみ機能します。
カーソルのFORループの詳細については、試してください ここ
これがそれを行う方法である場合、カーソル内のすべての行をフェッチすると暗黙的に閉じられ、それらの行のロックが解除されます
ロックは、カーソルをいつ閉じるか(または閉じるかどうか)に関係なく、トランザクションの期間中(つまり、コミットまたはロールバックを実行するまで)存在します。
私は行きます
declare
CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;;
v_1 c1%rowtype;
v_cnt number;
begin
open c_1;
select count(*) into v_cnt FROM MY_TABLE WHERE SALARY < 50000 and rownum < 3;
if v_cnt < 2 then
raise_application_error(-20001,'...');
end if;
--other processing
close c_1;
end;
カーソルが開かれてから(行がロックされて)から選択カウントまでの間に、誰かが50000未満の給与で1つ以上の行をテーブルに挿入する可能性はごくわずかです。その場合、アプリケーションエラーが発生しますが、カーソルはカーソルが開かれたときに存在する行のみを処理します。それが心配な場合は、最後にc_1%rowcountで別のチェックを行い、その問題が発生した場合は、セーブポイントにロールバックする必要があります。
複数の行が返されるたびに失敗したい場合は、次のことを試してください。
declare
l_my_table_rec my_table%rowtype;
begin
begin
select *
from my_table
into l_my_table_rec
where salary < 50000
for update;
exception
when too_many_rows then
-- handle the exception where more than one row is returned
when no_data_found then
-- handle the exception where no rows are returned
when others then raise;
end;
-- processing logic
end;
トランザクションを開始して、SELECT COUNT(*)MY_TABLE WHERE SALARY <50000が1より大きいかどうかを確認できます。
カーソルを反復処理する前にセーブポイントを作成し、返されるレコードが2つ未満の場合は部分的なロールバックを使用します。