1

こんにちは、それぞれに 100 万行の 2 つのテーブルがあります。私は oracle 11 g R1 を持っています。私たちの多くがこの状況を経験したに違いないと確信しています。

値が異なるテーブルから別のテーブルに更新する最も効率的で高速な方法は何ですか。

例: 表 1 には、精度の高い 4 つの NUMBER 列があります。例: 0.2212454215454212

表 2 には 6 つの列があります。両方のテーブルの共通の列に基づいて、テーブル 2 の 4 つの列を更新します。

私はこのようなものを持っています

DECLARE
TYPE test1_t IS TABLE OF test.score%TYPE INDEX BY PLS_..;
TYPE test2_t IS TABLE OF test.id%TYPE INDEX BY PLS..; 
TYPE test3_t IS TABLE OF test.Crank%TYPE INDEX BY PLS..;

vscore test1_t;
vid test2_t;
vurank test4_t;

BEGIN
  SELECT id,score,urank
    BULK COLLECT INTO vid,vscore,vurank
    FROM test;

  FORALL i IN 1 .. vid.COUNT
    MERGE INTO final T
      USING (SELECT vid (i) AS o_id,
                    vurank (i) AS o_urank,
                    vscore (i) AS o_score FROM DUAL) S
      ON (S.o_id = T.id)
    WHEN MATCHED THEN
      UPDATE SET T.crank = S.o_crank
      WHERE T.crank <> S.o_crank;

数値が高精度なので遅くなっていませんか?

Bulk Collect と Merge の組み合わせを試してみましたが、100 万行を更新する必要がある場合、最悪のシナリオではまだ 30 分ほどかかります。

ローイドに何かありますか?助けていただければ幸いです。

4

4 に答える 4

1

使用されている構文に焦点を当て、ロジックをスキップします(純粋な更新 + 純粋な挿入を使用すると問題が解決する可能性があります。マージ コスト、インデックス、マージ時のフル スキャンの可能性など)バルクコレクト構文で制限を 使用
する必要があります。制限なし

  1. すべてのレコードがメモリにロードされます
  2. 部分的にコミットされたマージがない場合、プロセスの最後に適用する必要がある大きなやり直しログが作成されます。

どちらもパフォーマンスが低下します。

DECLARE
 v_fetchSize NUMBER := 1000; -- based on hardware, design and .... could be scaled
 CURSOR a_cur IS 
 SELECT id,score,urank FROM test;    
 TYPE myarray IS TABLE OF a_cur%ROWTYPE;
 cur_array myarray;

    BEGIN
      OPEN a_cur;
      LOOP
        FETCH a_cur BULK COLLECT INTO cur_array LIMIT v_fetchSize;
          FORALL i IN 1 .. cur_array.COUNT
          // DO Operation
          COMMIT;
        EXIT WHEN a_cur%NOTFOUND;
      END LOOP;
      CLOSE a_cur;
    END;
于 2013-06-18T11:55:45.293 に答える
0
  1. 念のために言っておきますが、インデックスを作成する必要がありますtest.idfinal.id

  2. 最初select ... from testに取得したレコードが多すぎてTable 1、その後、すべてのレコードを のレコードと比較する必要がありますTable 2。更新する必要があるものだけを選択してください。したがって、少なくとも 2 つのバリアントがあります。

a) 変更されたレコードのみを選択:

  SELECT source_table.id, source_table.score, source_table.urank 
  BULK COLLECT INTO vid,vscore,vurank 
  FROM 
    test source_table, 
    final destination_table
  where 
    source_table.id = destination_table.id 
    and
    source_table.crank <> destination_table.crank
   ;

b) 日時の値を持つ新しいフィールドをソース テーブルに追加し、トリガーに現在の時刻を入力します。ピックの同期中に、最終日に変更されたレコードのみを選択します。このフィールドには索引を付ける必要があります。

更新フェーズでこのような変更を行った後は、他のフィールドを比較する必要はなく、ID のみを照合します。

  FORALL i IN 1 .. vid.COUNT 
  MERGE INTO FINAL T 
  USING (
    SELECT vid (i) AS o_id,
           vurank (i) AS o_urank,
           vscore (i) AS o_score FROM DUAL
  ) S 
  ON (S.o_id = T.id) 
  WHEN MATCHED 
  THEN UPDATE SET T.crank = S.o_crank 

ソースからタイム スライスに分割されたレコードを取得し、すべてのスライスを更新した後に変更をコミットb)できるため、元に戻す/やり直しセグメントのサイズが心配な場合は、バリアントの方が便利です。Table 1例: 00:00 から 01:00 、01:00 から 02:00 など。このバリアントでは、許容可能なサイズの REDO/UNDO ログを維持しながら、行内のコレクションにデータを選択することなく、SQL ステートメントだけで更新を実行できます。

于 2013-06-17T23:04:22.810 に答える