3

ばかげた質問のように聞こえるかもしれませんが、十分に明確にしていただければ幸いです。

  1. Spaghetti Codeについて話すとき、その基本は GOTO の使用法です。コードの最後にブレークポイントを置き、このブレークポイントに毎回到達しない場合、何かが間違っていると言うピアがありました。
  2. それにもかかわらず、Oracleパッケージ内で構造を使用することは一般的です(そして私が言うには、ルールです)EXIT WHEN (通常はテストが続きます%NOTFOUND )。

usingEXITがプログラミングの流れを壊すのは当然として、1 と 2 で一致しないものではありませんか?

PL/SQLでプログラミングをしている人は皆、悪い習慣に従っていますか? PL/SQL は、条件のこの特定のパターンに従っていませんか?

そのようなステートメントを使用するオラクルのフードの下にパフォーマンス上の理由はありますか?

この質問が既にされている場合は申し訳ありませんが、似たようなものは見つかりませんでした。

4

2 に答える 2

2

コードをシンプルにしておくことを強くお勧めします。PL/SQL の第一人者が次のように述べていることをお伝えできます。

注 : CURSOR-FOR-LOOP の使用が推奨されない場合があります。必要に応じて、単一の SELECT-INTO または BULK COLLECT を選択するもう 1 つのインテリジェントな方法を検討できます。

出典 : On Cursor FOR Loops、Oracle Magazine、Steven Feuerstein 著

(参照: Feuerstein、トップ 20 の PL/SQL のヒントとテクニック):

ループ

12. カーソル FOR ループを利用します。

カーソル FOR ループは、私のお気に入りの PL/SQL 構造の 1 つです。これは、言語の手続き面と SQL データベース言語の力との緊密かつ効果的な統合を十分に活用します。カーソルからデータを取得するために記述する必要があるコードの量が削減されます。これにより、プログラミングでループ エラーが発生する可能性が大幅に減少します。ループは、プログラムの中でエラーが発生しやすい部分の 1 つです。このループはうますぎるように聞こえますか? そうではありません - それはすべて本当です!

私のペットホテルである Share-a-Din-Din Inn に滞在するすべてのペットの請求書を更新する必要があるとします。以下の例には、カーソル occupancy_cur を使用して旅館のすべての居住者の部屋番号とペット ID 番号を選択する無名ブロックが含まれています。プロシージャ update_bill は、そのペットの部屋料金に新しい変更を追加します。

DECLARE
   CURSOR occupancy_cur IS
    SELECT pet_id, room_number
    FROM occupancy
    WHERE occupied_dt = SYSDATE;
   occupancy_rec occupancy_cur%ROWTYPE;
BEGIN
    OPEN occupancy_cur;
    LOOP
    FETCH occupancy_cur
      INTO occupancy_rec;
      EXIT WHEN occupancy_cur%NOTFOUND;
      update_bill
      (occupancy_rec.pet_id,
      occupancy_rec.room_number);
    END LOOP;
    CLOSE occupancy_cur;
END;

このコードは、想像の余地がありません。カーソルの定義 (2 行目) に加えて、カーソルのレコードを明示的に宣言し (5 行目)、カーソルを開き (7 行目)、無限ループを開始し、カーソル セットからレコードに行をフェッチする必要があります ( 9 行目)、cursor 属性で end-of-data 条件をチェックし (10 行目)、最後に更新を実行します。すべて完了したら、忘れずにカーソルを閉じる必要があります (14 行目)。この PL/SQL ブロックをカーソル FOR ループを使用するように変換すると、次のようになります。

DECLARE
CURSOR occupancy_cur IS
  SELECT pet_id, room_number
  FROM occupancy WHERE occupied_dt =
  SYSDATE;
BEGIN
  FOR occupancy_rec IN occupancy_cur
  LOOP
    update_bill (occupancy_rec.pet_id,
    occupancy_rec.room_number);
  END LOOP;
END;

ここで、カーソル FOR ループの美しい単純さがわかります。レコードの宣言はなくなりました。OPEN、FETCH、および CLOSE ステートメントはなくなりました。%FOUND 属性をチェックする必要はなくなりました。すべてを正しく行うという心配はなくなりました。代わりに、PL/SQLに対して次のように言います: 「あなたも私も、各行が必要であり、その行をカーソルに一致するレコードにダンプしたいことを知っています。それは私に任せてくれませんか?」そして、SQL と統合された最新のプログラミング言語と同じように、PL/SQL はそれを処理します。

于 2012-10-31T01:10:56.777 に答える
2

はい、多くの人が悪い習慣に従っています。

悪いスタイル

OPEN/FETCH/CLOSE が完全に不要なコードを追加するという @Osy に同意します。さらに言えば、 はほとんど使用すべきではありませんCURSOR

まず第一に、通常はプレーン SQL で可能な限り多くのことを行いたいと考えます。PL/SQL を使用する必要がある場合は、暗黙カーソルを使用してください。コードの行を節約し、関連するロジックをより近くに保つのに役立ちます。

私は、個々のコード単位をできるだけ小さく保つことを強く信じています。一見すると、これを行うのCURSORに役立つように思えます。SQL を 1 か所で定義し、後で PL/SQL ループを実行できます。

しかし、実際には、その余分な間接的な層はほとんど価値がありません。多くのロジックが SQL にある場合もあれば、多くのロジックが PL/SQL にある場合もあります。しかし実際には、両方に多くの複雑なロジックを配置することはほとんど意味がありません。コードは通常、次のいずれかのようになります。

for records in (<simple SQL>) loop
    <complex PL/SQL>
end loop;

また:

for records in
(
    <complex SQL>
) loop
    <simple PL/SQL>;
end loop;

いずれにしても、コード セクションの 1 つが非常に小さくなります。コードのこれら 2 つのセクションを分離することの複雑さは、より大きな単一のコード セクションの複雑さよりも大きくなります。(しかし、それは明らかに私の意見です。)

悪いパフォーマンス

OPEN/FETCH/CLOSE を使用すると、パフォーマンスに重大な影響があります。この方法は、カーソル for ループまたは暗黙カーソルを使用するよりもはるかに遅くなります。

コンパイラは、一部の for ループで一括収集を自動的に使用できます。ただし、Oracle のプレゼンテーション「PL/SQL Performance—Debunking the Myths」の 122 ページから引用すると、次のようになります。

オープン、フェッチ ループ、クローズ フォームを使用して、このチャンスを無駄にしないでください。

簡単な例を次に示します。

--Sample data
create table t(a number, b number);
insert into t select level, level from dual connect by level <= 100000;
commit;

--OPEN/FETCH/CLOSE
--1.5 seconds
declare
    cursor test_cur is
    select a, b from t;
    test_rec test_cur%rowtype;
    counter number;
begin
    open test_cur;
    loop
        fetch test_cur into test_rec;
        exit when test_cur%notfound;
        counter := counter + 1;
    end loop;
    close test_cur;
end;
/

--Implicit cursor
--0.2 seconds
declare
    counter number;
begin
    for test_rec in (select a, b from t) loop
        counter := counter + 1;
    end loop;
end;
/
于 2012-10-31T04:37:10.487 に答える