1

約 300000 行を超えるカーソルでいくつかの挿入を行う必要がありますが、これはゆっくりと実行されています。より速く実行する方法についてのアイデアはありますか? コミットをバッチ処理することで高速化できますか? たとえば、1000行目の後にコミットを実行します

DECLARE

  CURSOR test_cursor IS 
    SELECT a from database.mytable
BEGIN

  FOR curRow IN test_cursor LOOP

    insert into tableb (testval)
    values ('something');

  commit;

  END LOOP;
END; 
4

4 に答える 4

9

300000 行はそれほど多くの行ではありません。行がそれぞれ非常に大きい場合を除き、バッチの途中でコミットしないでください。

中間コミットは以下のみを達成します:

  • 各コミットが追加の作業を作成するため、追加のオーバーヘッド
  • エラーが発生した場合の再起動可能性の喪失 (およびトランザクションの整合性の喪失)、
  • ORA-1555 が発生する可能性が高くなります

プロセスが実際にループ内に単一の挿入があるカーソルである場合は、単一のステートメントを実行する必要があります。

BEGIN
   INSERT INTO tableb (col1..coln) (SELECT col1..coln FROM database.mytable);
END;

それでも追加のパフォーマンスが必要な場合は、直接挿入と並列操作を調べることができますが、「わずか」300k 行で過剰に最適化されている可能性があります。

利用できる最大の最適化の 1 つは、単一行ステートメントのバッチで構成される従来の手続き型アプローチではなく、セットの観点から考えることです。

于 2012-06-01T08:45:57.840 に答える
3

または、これを試すことができます:

DECLARE
  CURSOR test_cursor IS 
    SELECT col1 from table_a;

  TYPE fetch_array IS TABLE OF test_cursor%ROWTYPE;
  test_array fetch_array;

  l_errors                              PLS_INTEGER;
  l_dml_errors                          EXCEPTION;
  PRAGMA EXCEPTION_INIT(l_dml_errors, -24381);    

BEGIN

  open test_cursor;
  loop
    fetch test_cursor bulk collect into test_array limit 10000;
    forall i in 1..test_array.count save exceptions
      insert into table_b(col1)
      values(test_array(i).col1);
      exit when test_cursor%notfound;
  end loop;
  close test_cursor;
  commit;

EXCEPTION 
WHEN l_dml_errors THEN
    l_errors := SQL%BULK_EXCEPTIONS.COUNT;
    dbms_output.put_line('Number of INSERT statements that failed: ' || l_errors);
    FOR i IN 1 .. l_errors
    LOOP
      dbms_output.put_line('Error #' || i || ' at '|| 'iteration #' || SQL%BULK_EXCEPTIONS(i).ERROR_INDEX);
      dbms_output.put_line('Error message is ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
    END LOOP;
END; 
于 2012-06-01T08:21:55.767 に答える
0

これにはカーソルアプローチはお勧めしません。このような状況では、並列ヒントの追加を使用します。ほとんどの場合、クエリは文字通り N 倍の速度で実行されます。N は並列度です。nologging / noarchivelog を使用してディザスタ リカバリをバイパスすることをお勧めします。

本当に大規模な移行 (数十から数百 GB) の場合、テーブルの自然キー (通常は日付) をバッチ処理することをお勧めします。その周りの少量の状態により、必要に応じて移行をキャンセルして再開することができます。

于 2012-06-01T06:04:06.377 に答える
-1

これが役立つかもしれません。これを試してください

DECLARE
i number;
  CURSOR test_cursor IS 
    SELECT a from database.mytable
BEGIN

  FOR curRow IN test_cursor LOOP

    insert into tableb (testval)
    values ('something');
i:i+1;
if mod(i,1000)=0 then
  commit;
end if;
  END LOOP;
commit;
END; 
于 2012-06-01T06:06:51.847 に答える