2012-02-14 8 views
1

私はかなり新しいSQLで、誰かが私を助けてくれるのだろうかと思っていました。1000万件以上のレコードを持つ高速更新データベース

約1,000万行のデータベースがあります。

いくつかのNULLフィールドを持つレコードを見つけて、特定の値に更新するスクリプトを作成する必要があります。

私が単純な更新ステートメントを実行することから得られる問題は、ロールバックスペースを吹き飛ばすことです。

私は、BULK COLLECT AND FETCHを使用する必要があることを読んでいました。

私の考えは、一度に10,000レコードを取り出し、更新し、コミットして、引き続き取り出すことでした。

私はGoogleで例題を探してみましたが、まだ何も見つかりませんでした。

助けが必要ですか?

ありがとうございます!

これは私がこれまで持っているものです。

DECLARE 
    CURSOR rec_cur IS 
    SELECT DATE_ORIGIN 
    FROM MAIN_TBL WHERE DATE_ORIGIN IS NULL; 

    TYPE date_tab_t IS TABLE OF DATE; 

    date_tab date_tab_t; 

BEGIN 
    OPEN rec_cur; 
    LOOP 
     FETCH rec_cur BULK COLLECT INTO date_tab LIMIT 1000; 
     EXIT WHEN date_tab.COUNT() = 0; 

     FORALL i IN 1 .. date_tab.COUNT 
      UPDATE MAIN_TBL SET DATE_ORIGIN = '23-JAN-2012' 
      WHERE DATE_ORIGIN IS NULL; 

    END LOOP; 
    CLOSE rec_cur; 
END; 
+6

したがって、単一のUPDATEステートメントを試してみましたが、エラーが発生しましたか?正確なエラーは何でしたか?エラーがあなたが 'UNDO'スペースから外れていた場合は、' UNDO'スペースを増やすようにDBAに依頼しましたか? 1,000万行は大したことではありません。あなたのデータベースは、数MBのディスク容量を節約するコードを書くのではなく、妥当な作業負荷を処理するためのサイズにする必要があります。 –

+0

あなたは正しい道にいると思います。いくつかのコードを投稿すると、誰かがあなたのためにそれを批評することができるかもしれません。 –

+0

データベースはプロダクションサーバーなので、まだ単一の更新ステートメントを実行していませんが、私の上司の経験から、以前はクラッシュしています...一括フェッチスタイルでも可能ですか? –

答えて

3

私は私はあなたが何をしようとして見ると思います。下のコードとあなたのコードの違いについては、いくつかの点を述べたいと思います。

  1. あなたforallループは、インデックスを使用しません。これは、テーブルを更新するためにrowidを使用することで簡単に回ることができます。
  2. forallの後にコミットすることで、必要な元に戻す量を減らすことができます。何か問題が生じた場合にロールバックすることをより困難にします。論理的には、クエリーを簡単に中間で再開することができますが、目的を損なうことはありません。
  3. rowidsが小さく、一度に少なくとも25kを収集する。 100kでない場合。
  4. Oracleでは、nullを索引付けできません。あなたがより多くの情報を必要としていることについては、stackoverflowに関する多くの質問があります。たとえば、nvl(date_origin,'x')のような機能的なインデックスは、データを選択する速度を向上させます。また、実際にはテーブル自体を使用する必要はありません。インデックスからのみ選択します。
  5. 日付のデータ型は文字列のようです。私はこれを保ったが、それは賢明ではない。
  6. 誰かにUNDO表領域サイズを増やすことができれば、まっすぐな更新がより迅速になります。私は、現時点ではDBにアクセスすることはできませんが、amdiOaas実行を見つけるために

    nvl(date_origin,to_date('absolute_minimum_date_in_Oracle_as_a_string','yyyymmdd')) 
    

    date_originは、インデックスのようなものであるべき日であるあなたのコメントのとおりと仮定すると、

次のクエリ:

select to_date('0001','yyyy') from dual; 

これは役に立つエラーです。


PL/SQL Developerの使用例。

create table main_tbl as 
    select cast(null as date) as date_origin 
    from all_objects 
     ; 

create index i_main_tbl 
    on main_tbl (nvl(to_date(date_origin,'yyyy-mm-dd') 
        , to_date('0001-01-01' ,'yyyy-mm-dd')) 
       ) 
     ; 

declare 

    cursor c_rec is 
    select rowid 
     from main_tbl 
    where nvl(date_origin,to_date('0001-01-01','yyyy-mm-dd')) 
       = to_date('0001-01-01','yyyy-mm-dd') 
      ; 

    type t__rec is table of rowid index by binary_integer; 
    t_rec t__rec; 

begin 

    open c_rec; 
    loop 

     fetch c_rec bulk collect into t_rec limit 50000; 

     exit when t_rec.count = 0; 

     forall i in t_rec.first .. t_rec.last 
     update main_tbl 
      set date_origin = to_date('23-JAN-2012','DD-MON-YYYY') 
      where rowid = t_rec(i) 
       ; 
     commit ; 

    end loop; 
    close c_rec; 
end; 
/
+0

感謝のベン! t_rec.firstでコードがforall iに爆破されている何らかの理由で、これは素晴らしいアプローチのようです。t_rec.last loop PLS-00103:次のいずれかが予想されるときにシンボル "LOOP"を検出しました: –

+0

@xela_austinそれは、私の最も一般的なオラクルのミスを犯しました。 'forall'は' loop'を必要としません。私は私の答えを更新しました。詳細については、[here](http://psoug.org/reference/array_processing.html)を参照してください。 – Ben

+0

もう一度ありがとう! ORA-01858:数値ではない数字の文字が見つかりました。これはt_rec limit 50000にfetch c_rec bulk collectで発生します。 –

関連する問題