web-dev-qa-db-ja.com

Oracleデータベースへの一括挿入:FOR Cursorループと単純なSelectのどちらが良いですか?

Oracleデータベースへの一括挿入のより良いオプションはどれですか?のようなFOR Cursorループ

DECLARE
   CURSOR C1 IS SELECT * FROM FOO;
BEGIN
   FOR C1_REC IN C1 LOOP
   INSERT INTO BAR(A,
                B,
                C)
          VALUES(C1.A,
                 C1.B,
                 C1.C);
   END LOOP;
END

または次のような単純な選択:

INSERT INTO BAR(A,
                B,
                C)
        (SELECT A,
                B,
                C
        FROM FOO);

どちらかが優れている特定の理由はありますか?

23
Sathyajith Bhat

カーソルに時間がかかるため、選択オプションをお勧めします。
また、選択を使用すると、クエリを変更する必要がある人にとってもわかりやすくなります。

29
Josh Mein

一般的な経験則は、PL/SQLを使用する代わりに単一のSQLステートメントを使用して実行できる場合、そうする必要があります。通常はより効率的です。

ただし、プロシージャロジックを(何らかの理由で)追加する必要がある場合は、PL/SQLを使用する必要がありますが、行ごとの処理ではなく一括操作を使用する必要があります。 (注:Oracle 10g以降では、FORループは自動的にBULK COLLECTを使用して一度に100行をフェッチしますが、挿入ステートメントは行ごとに実行されます)。

例えば.

DECLARE
   TYPE tA IS TABLE OF FOO.A%TYPE INDEX BY PLS_INTEGER;
   TYPE tB IS TABLE OF FOO.B%TYPE INDEX BY PLS_INTEGER;
   TYPE tC IS TABLE OF FOO.C%TYPE INDEX BY PLS_INTEGER;
   rA tA;
   rB tB;
   rC tC;
BEGIN
   SELECT * BULK COLLECT INTO rA, rB, rC FROM FOO;
   -- (do some procedural logic on the data?)
   FORALL i IN rA.FIRST..rA.LAST
      INSERT INTO BAR(A,
                      B,
                      C)
      VALUES(rA(i),
             rB(i),
             rC(i));
END;

上記には、SQLとPL/SQL間のコンテキスト切り替えを最小限に抑えるという利点があります。 Oracle 11gでは、レコードのテーブルのサポートも強化されているため、列ごとに個別のPL/SQLテーブルを用意する必要はありません。

また、データの量が非常に多い場合、バッチでデータを処理するようにコードを変更することができます。

22
Jeffrey Kemp

ロールバックセグメント/アンドゥセグメントがトランザクションのサイズに対応できる場合、オプション2の方が適しています。オプション1は、必要なロールバック容量がなく、大きな挿入を小さなコミットに分割して、ロールバック/セグメントの小さすぎるエラーが発生しないようにする必要がある場合に便利です。

5
MichaelN

2番目のオプションのような単純な挿入/選択がはるかに望ましいです。 1番目のオプションの挿入ごとに、pl/sqlからsqlへのコンテキストスイッチが必要です。 trace/tkprofでそれぞれを実行し、結果を調べます。

Michaelが言及しているように、ロールバックでステートメントを処理できない場合は、dbaに詳細を教えてください。ディスクは安価ですが、データを複数のパスに挿入することで得られる部分的な結果は潜在的に非常に高価です。 (挿入に関連する取り消しはほとんどありません。)

5
Scott Swank

この質問には重要な情報が欠けていると思います。

いくつのレコードを挿入しますか?

  1. 1からccaの場合。 10.000では、SQLステートメントを使用する必要があります(彼らが理解しやすく、記述しやすいと言っているように)。
  2. CCAからの場合。 10.000からcca。 100.000の場合、カーソルを使用する必要がありますが、10.000レコードごとにコミットするロジックを追加する必要があります。
  3. CCAからの場合。 100,000から数百万の場合、バルク収集を使用してパフォーマンスを向上させます。
3
sulica

他の回答を読むとわかるように、多くのオプションが利用可能です。 1万行未満の場合は、2番目のオプションを選択する必要があります。

要するに、約10kから<100kと言ってもいいほどです。それは一種の灰色の領域です。多くの古いジーザーが大きなロールバックセグメントでbarえます。しかし、正直なところ、ハードウェアとソフトウェアは、コードを数回実行するだけで、多くのレコードでオプション2を使用できるようになる可能性があります。そうでない場合は、おそらく1k〜10k行ごとにコミットする必要があります。ここに私が使用するスニペットがあります。短いのでカーソルを宣言する必要がないので気に入っています。さらに、一括収集と全体の利点があります。

begin
    for r in (select rownum rn, t.* from foo t) loop
        insert into bar (A,B,C) values (r.A,r.B,r.C);
        if mod(rn,1000)=0 then
            commit;
        end if;
    end;
    commit;
end;

私はこれを見つけました link Oracleサイトでオプションの詳細を説明しています。

2

毎日のデータの完全なリロードは行いません。たとえば、デンバーのサイトを読み込んでいるとします。ほぼリアルタイムのデルタには他の戦略があります。

バルクロードとほぼ同じ速度であることがわかったため、create table SQLを使用します。たとえば、以下のcreate tableステートメントを使用してデータをステージングし、必要な正しいデータ型に列をキャストします。

CREATE TABLE sales_dataTempを選択キャスト(日付としてcolumn1)としてSALES_QUARTERとして、キャスト(数値として販売)をSALES_IN_MILLIONSとして、.... FROM TABLE1;

この一時テーブルは、サイトによってパーティション分割されたリストであるターゲットテーブルの構造を正確に反映しています。次に、DENVERパーティションとパーティションスワップを行い、新しいデータセットを取得します。

0
Hughsmg

次を使用できます。

Bulk bindingと呼ばれるFOR ALLと一緒に一括収集します。

PL/SQL forall演算子は、単純な表の挿入で30倍速くなるためです。

BULK_COLLECTとOracle FORALLを合わせて、これら2つの機能はBulk Bindingとして知られています。バルクバインドは、複数の個別のSELECTINSERTUPDATEまたはDELETEステートメントの代わりに、取得または保存するために実行されるPL/SQLテクニックです。テーブルのデータは、すべての操作が一度に一括で実行されます。これにより、一度に1行ずつ個別にアクセスするときに、PL/SQLエンジンがSQLエンジンに渡され、PL/SQLエンジンに戻されるなどの場合に発生するコンテキスト切り替えが回避されます。 INSERTUPDATE、およびDELETEステートメントで一括バインドを行うには、SQLステートメントをPL/SQL FORALLステートメントで囲みます。 SELECTステートメントで一括バインドを行うには、SELECTを使用する代わりに、INTOステートメントにBULK COLLECT句を含めます。

パフォーマンスが向上します。

0
user2001117