web-dev-qa-db-ja.com

Oracle PL / SQLカーソルのレコード数を確認するにはどうすればよいですか?

これが私のカーソルです:

CURSOR C1 IS SELECT * FROM MY_TABLE WHERE SALARY < 50000 FOR UPDATE;

手順の間、これらのレコードをロックするために、すぐにカーソルを開きます。

カーソルに2つ未満のレコードがある場合に、アプリケーションエラーを発生させたい。 C1%ROWCOUNTプロパティの使用は、これまでにフェッチされた数のみをカウントするため失敗します。

このユースケースに最適なパターンは何ですか?ダミーのMY_TABLE%ROWTYPE変数を作成し、カーソルをループしてそれらをフェッチしてカウントを維持する必要がありますか、それとももっと簡単な方法がありますか?これがその方法である場合、カーソル内のすべての行をフェッチすると暗黙的に閉じてロックが解除されますか、それともすべてをフェッチしたとしても明示的に閉じるまで開いたままになりますか?

この数を超える他のさまざまなタスクのために、カーソルが開いたままであることを確認する必要があります。

8
aw crud

注意:質問を読み直しました。レコードが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ループの詳細については、試してください ここ

6
ShoeLace

これがそれを行う方法である場合、カーソル内のすべての行をフェッチすると暗黙的に閉じられ、それらの行のロックが解除されます

ロックは、カーソルをいつ閉じるか(または閉じるかどうか)に関係なく、トランザクションの期間中(つまり、コミットまたはロールバックを実行するまで)存在します。

私は行きます

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で別のチェックを行い、その問題が発生した場合は、セーブポイントにロールバックする必要があります。

1
Gary Myers

複数の行が返されるたびに失敗したい場合は、次のことを試してください。

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;
1
Adam Musch

トランザクションを開始して、SELECT COUNT(*)MY_TABLE WHERE SALARY <50000が1より大きいかどうかを確認できます。

0
Dmitri Kouminov

カーソルを反復処理する前にセーブポイントを作成し、返されるレコードが2つ未満の場合は部分的なロールバックを使用します。

0
a'r