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);
どちらかが優れている特定の理由はありますか?
カーソルに時間がかかるため、選択オプションをお勧めします。
また、選択を使用すると、クエリを変更する必要がある人にとってもわかりやすくなります。
一般的な経験則は、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テーブルを用意する必要はありません。
また、データの量が非常に多い場合、バッチでデータを処理するようにコードを変更することができます。
ロールバックセグメント/アンドゥセグメントがトランザクションのサイズに対応できる場合、オプション2の方が適しています。オプション1は、必要なロールバック容量がなく、大きな挿入を小さなコミットに分割して、ロールバック/セグメントの小さすぎるエラーが発生しないようにする必要がある場合に便利です。
2番目のオプションのような単純な挿入/選択がはるかに望ましいです。 1番目のオプションの挿入ごとに、pl/sqlからsqlへのコンテキストスイッチが必要です。 trace/tkprofでそれぞれを実行し、結果を調べます。
Michaelが言及しているように、ロールバックでステートメントを処理できない場合は、dbaに詳細を教えてください。ディスクは安価ですが、データを複数のパスに挿入することで得られる部分的な結果は潜在的に非常に高価です。 (挿入に関連する取り消しはほとんどありません。)
この質問には重要な情報が欠けていると思います。
いくつのレコードを挿入しますか?
他の回答を読むとわかるように、多くのオプションが利用可能です。 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サイトでオプションの詳細を説明しています。
毎日のデータの完全なリロードは行いません。たとえば、デンバーのサイトを読み込んでいるとします。ほぼリアルタイムのデルタには他の戦略があります。
バルクロードとほぼ同じ速度であることがわかったため、create table SQLを使用します。たとえば、以下のcreate tableステートメントを使用してデータをステージングし、必要な正しいデータ型に列をキャストします。
CREATE TABLE sales_dataTempを選択キャスト(日付としてcolumn1)としてSALES_QUARTERとして、キャスト(数値として販売)をSALES_IN_MILLIONSとして、.... FROM TABLE1;
この一時テーブルは、サイトによってパーティション分割されたリストであるターゲットテーブルの構造を正確に反映しています。次に、DENVERパーティションとパーティションスワップを行い、新しいデータセットを取得します。
次を使用できます。
Bulk binding
と呼ばれるFOR ALLと一緒に一括収集します。
PL/SQL forall
演算子は、単純な表の挿入で30倍速くなるためです。
BULK_COLLECT
とOracle FORALL
を合わせて、これら2つの機能はBulk Binding
として知られています。バルクバインドは、複数の個別のSELECT
、INSERT
、UPDATE
またはDELETE
ステートメントの代わりに、取得または保存するために実行されるPL/SQLテクニックです。テーブルのデータは、すべての操作が一度に一括で実行されます。これにより、一度に1行ずつ個別にアクセスするときに、PL/SQLエンジンがSQLエンジンに渡され、PL/SQLエンジンに戻されるなどの場合に発生するコンテキスト切り替えが回避されます。 INSERT
、UPDATE
、およびDELETE
ステートメントで一括バインドを行うには、SQLステートメントをPL/SQL FORALL
ステートメントで囲みます。 SELECT
ステートメントで一括バインドを行うには、SELECT
を使用する代わりに、INTO
ステートメントにBULK COLLECT
句を含めます。
パフォーマンスが向上します。