2

Oracle 外部テーブルから多くの選択を実行する必要があります。

これによく似たカーソルが10個あります(ext_tempは外部テーブルです)

CURSOR F_CURSOR (day IN varchar,code Number,orig Number)
    IS
    select NVL(sum(table_4.f),0) 
     from ext_temp table_4
    where
      --couple of conditions here, irrelevant for the question at hand.
      AND TO_CHAR(table_4.day,'YYYYMMDD') = day
      AND table_4.CODE = code
      AND table_4.ORIG = orig;

また、外部テーブルには約 22659 個のレジスタがあります。

私のスクリプトのメインループは次のようになります

   for each register in some_query: --22659 registers
       open F_cursor(register.day,register.code,register.orig);
       --open 9 more cursors

       fetch F_cursor into some_var;  
       --fetch 9 more cursors, with the same structure

クエリは多くのものに取って代わられています。ここから、インデックスや DML を使用できないことがわかります。

それで、それをより速く実行する方法はありますか?plsql スクリプトを書き直すことはできますが、時間が残っているとは思えません。

更新: 重要な詳細を見逃しました。

私はデータベースの所有者または DBA ではありません。その男はデータベースに余分な情報 (約 3 GB のデータ) を必要とせず、外部テーブルしか彼から得ることができませんでした。彼は私たちが一時テーブルを作成することを許可していません。彼の理由に疑問を呈するつもりはありませんが、外部テーブルはこれに対する解決策ではありません。だから、私たちはそれらにこだわっています。

4

4 に答える 4

4

それらをOracleテーブルにします。

SQL*LOADER外部テーブルは、日常的に作業するためではなく、置き換えるためにあります。

外部テーブルの内容をテーブルにロードする基になるファイルが変更されるたびに、インポート スクリプトを実行するだけですOracle

これがあなたの同名の考えです(ここから盗まれました):

の代わりに 外部テーブルを使用していますsqlldr

外部テーブルを使用すると、次のことができます

  • 1 つのステートメントでフラット ファイルを既存のテーブルとマージします。
  • うまく圧縮したいテーブルに途中でフラットファイルをソートします。
  • パラレル ダイレクト パス ロードを実行します。入力ファイルを分割したり、多数のスクリプトを作成したりする必要はありません。
  • sqlldrストアド プロシージャまたはトリガーから実行されます (insert is not sqlldr)
  • 複数テーブルの挿入を行う
  • クレンジング/変換のためにパイプライン化された plsql 関数を介してデータを流す

等々。それらは代わりに --そもそもsqlldr使用することなくデータベースにデータを取得するためのものです。sqlldr

通常、運用システムで毎日クエリを実行することはなく、データをロードするために使用します。

アップデート:

各クエリでフルスキャンを実行する必要がある3GBため、テーブルでまともなパフォーマンスが得られることはありません。これは、最初のクラスのディスク読み取りスピンドル移動フルスキャンであり、予定ですが、実際の実行時間にはほとんど気づきません。Oracle3GB

セッションが開始されるたびに、データを操作し、外部テーブルからデータをロードするために使用できる一時テーブルを作成するように男を説得してください。

これは、セッションごとにテーブルの個別のコピーを一時テーブルスペースに保持する必要があるため、最適なソリューションではありませんが、パフォーマンスの面でははるかに優れています。

于 2009-10-20T17:47:48.080 に答える
3

意味がないのに変えられない制限を回避しなければならない場合は、本当に大変です...

外部テーブルを一度読み込んでから、必要なデータをコード内のインデックスのようなデータ構造 (基本的には、探しているレジスタごとに 1 つの要素を持つ配列) に構築することをお勧めします。

したがって、カーソルは次のようになります。

CURSOR F_CURSOR (day IN varchar, orig IN Number)
    IS
    select NVL(sum(table_4.f),0) value, table_4.CODE register
     from ext_temp table_4
    where
      --couple of conditions here, irrelevant for the question at hand.
      AND TO_CHAR(table_4.day,'YYYYMMDD') = day
      -- AND table_4.CODE = code -- don't use this condition!
      AND table_4.ORIG = orig;

そして、レジスタループはカーソルループに変わります:

open F_cursor(register.day,register.orig);
LOOP
    fetch F_cursor into some_var;
    EXIT WHEN F_cursor%NOT_FOUND
    result (some_var.register) := some_var.value;
END LOOP;

その結果、レジスタごとに外部テーブルをループする代わりに、すべてのレジスタに対して 1 つのループが必要になります。

これは、あなたが言及した10個のカーソルに対して拡張できます。

于 2009-10-20T20:29:55.257 に答える
0

外部テーブルのデータを一時的にインデックスが作成された (必要に応じて) テーブルに書き込み、それに対して複数のクエリを実行できます。

create your_temp_table as select * from ext_temp;
create index your_desired_index on your_temp_table(indexed_field);

次に、your_temp_table を使用してすべてのクエリを直接実行します。

于 2009-10-20T17:47:46.677 に答える
0

ここでは、外部テーブルは適切な解決策ではないように見えるという Quassnoi の提案と、拘束されて船外に投げ出され、泳ぐように求められているという DCookie の例えに完全に同意しますが、少なくともプログラムを構造化する方法があるかもしれません。外部テーブルは 1 回だけ読み取られます。あなたの説明からの私の信念は、10個のカーソルすべてが外部テーブルから読み取っていることです。つまり、Oracleに外部テーブルを10回スキャンさせているということです。

この推論が正しいと仮定すると、最も簡単な答えは、IronGoofy が提案したものと同様に、外部テーブルを駆動カーソルにする可能性があります。some_query以下のコード スニペットの内容に応じて、

for each register in some_query

クエリが外部テーブルにあるのと同じ数の行を返すという事実が偶然ではないと仮定すると、最も簡単なオプションは次のようにすることです

FOR register in (select * from ext_temp)
LOOP
  -- Figure out if the row should have been part of cursor 1
  IF( <<set of conditions>> ) 
  THEN
    <<do something>>
  -- Figure out if the row should have been part of cursor 2
  ELSIF( ... )
  ...
END LOOP;

また

FOR register in (select * 
                   from ext_temp a, 
                        (<<some query>>) b 
                  where a.column_name = b.column_name )
LOOP
  -- Figure out if the row should have been part of cursor 1
  IF( <<set of conditions>> ) 
  THEN
    <<do something>>
  -- Figure out if the row should have been part of cursor 2
  ELSIF( ... )
  ...
END LOOP;

さらに一歩進んで、ロジックをカーソル (および IF ステートメント) から駆動カーソルに移動すると、より効率的になります。上記のコード スニペットの単純なものを使用します (もちろん、some_queryこれらの例に参加することもできます

FOR register in (select a.*,
                        NVL(sum( (case when condition1 and condition2
                                       then table_4.f
                                       else 0
                                       end) ),
                             0) f_cursor_sum
                  from ext_temp table_4)
LOOP
  <<do something>>
END LOOP;

これを行った後でも、まだ行ごとの処理を行っていることがわかった場合は、さらに一歩進んで、駆動カーソルからローカルに宣言されたコレクションに BULK COLLECT を実行し、そのコレクションを操作することもできます。ほとんどの場合、ローカル コレクションに 3 GB のデータをフェッチする必要はありません (ただし、PGA をクラッシュさせると、DBA は一時テーブルがそれほど悪いものではないと結論付ける可能性がありますが、私がアドバイスすることではありません)。 LIMIT 句を使用して一度に 100 行を処理すると、処理が少し効率的になります。

于 2009-10-20T21:36:03.973 に答える