2017-05-30 41 views
0

私は、あるスキーマのテーブルから別のスキーマのテーブル(同一データベース内の)にコピーする必要があるレコードを約500万本持っています。私はスクリプトを準備しましたが、それは私に以下のエラーを与えます。続きOracle PL/SQLでのバルク挿入

ORA-06502: PL/SQL: numeric or value error: Bulk bind: Error in define

エラーがどこにあるか見つけるために私を助けてもらえ

DECLARE  
    TYPE tA IS TABLE OF varchar2(10) INDEX BY PLS_INTEGER; 
    TYPE tB IS TABLE OF SchemaA.TableA.band%TYPE INDEX BY PLS_INTEGER; 
    TYPE tD IS TABLE OF SchemaA.TableA.start_date%TYPE INDEX BY PLS_INTEGER; 
    TYPE tE IS TABLE OF SchemaA.TableA.end_date%TYPE INDEX BY PLS_INTEGER;   
    rA tA; 
    rB tB; 
    rD tD; 
    rE tE; 
    f number :=0;  
BEGIN 

    SELECT col1||col2||col3 as main_col, band, effective_start_date as start_date, effective_end_date as end_date 
    BULK COLLECT INTO rA, rB, rD, rE 
    FROM schemab.tableb; 

    FORALL i IN rA.FIRST..rE.LAST 
     insert into SchemaA.TableA(main_col, BAND, user_type, START_DATE, END_DATE, roll_no) 
     values(rA(i), rB(i), 'C', rD(i), rE(i), 71); 

    f:=f+1; 

    if (f=10000) then 
     commit; 
    end if; 

end; 

私のスクリプトのですか?

答えて

1

スクリプトは私のために働いて、私は約5百万のデータを15分以内に読み込むことができました。代わりSchemaA.TableA.main_col%のタイプを宣言する

ALTER SESSION ENABLE PARALLEL DML 
/

DECLARE 


cursor c_p1 is 
    SELECT col1||col2||col3 as main_col, band, effective_start_date as start_date, effective_end_date as end_date 
    FROM schemab.tableb; 

    TYPE TY_P1_FULL is table of c_p1%rowtype 
    index by pls_integer; 

    v_P1_FULL TY_P1_FULL; 

    v_seq_num number; 

BEGIN 

open c_p1; 

loop 

fetch c_p1 BULK COLLECT INTO v_P1_FULL LIMIT 10000; 
exit when v_P1_FULL.count = 0; 
FOR i IN 1..v_P1_FULL.COUNT loop 


INSERT /*+ APPEND */ INTO schemaA.tableA VALUES (v_P1_FULL(i)); 

end loop; 
commit; 
end loop; 
close c_P1; 
dbms_output.put_line('Load completed'); 


end; 

-- Disable parallel mode for this session 
ALTER SESSION DISABLE PARALLEL DML 
/
0

以下にあなたのコードの最初の2行を変更した後に試してみてください:

DECLARE 

TYPE tA IS TABLE OF SchemaA.TableA.main_col%TYPE INDEX BY PLS_INTEGER; 
... 
... 

これが原因で、データ型/長さの不一致であってもよいです。宣言セクションでは、テーブルから型を継承することを宣言していません。

また、コミットのfロジックはあなたのために魔法をしません。より良いあなたがBULL COLLECT

+0

は、私はまた、データ型としてVARCHAR2(10)、日付などを与える試みたが、まだそれは同じエラーを得ました。 – Nik

4

でLIMITを使用すべき理由はない、単純な

insert into SchemaA.TableA (main_col, BAND, user_type, START_DATE, END_DATE, roll_no) 
SELECT col1||col2||col3 as main_col, band, 'C', effective_start_date, effective_end_date, 71 
FROM schemab.tableb; 

この

f:=f+1; 
if (f=10000) then 
    commit; 
end if; 

はどんな意味がありません。 fは1になります。 f=10000は決して真ではないので、あなたはCOMMITを作成しません。

+0

したがって、10kレコードごとにコミットを発行する場合は、何を変更する必要がありますか。 – Nik

+0

また、パフォーマンスが向上するため、一括挿入を使用しています。 – Nik

+2

'INSERT ... SELECT'を使うと、文脈の切り替えを避けることができます。なぜ10kレコード後にコミットしたいのですか?挿入が遅くなることがあります。 –

1

ORA-06502: PL/SQL: numeric or value error: Bulk bind: Error in define

あなたがINSERTVALUES句にリテラルを持っているので、あなたはそのエラーを取得します。 FORALLはすべてが配列にバインドされることを期待しています。

プログラムには文字通り大きな問題があります。 BULK COLLECT句にはLIMITがありません。そのため、TableBから500万レコードをすべてコレクションにロードしようとします。それはあなたのセッションのメモリ制限を吹き飛ばします。

BULK COLLECTFORALLの使用のポイントは、より大きなデータセットのチャンクを一括して処理し、バッチで処理することです。そのためにはループが必要です。ループにはFOR条件がありません。代わりに、フェッチで何かが返されたかどうかをテストし、配列にレコードがない場合は終了します。

DECLARE  
    TYPE recA IS RECORD (
     main_col SchemaA.TableA.main_col%TYPE 
     , band SchemaA.TableA.band%TYPE 
     , start_date date 
     , end_date date 
     , roll_ni number); 
    TYPE recsA is table of recA 
    nt_a recsA; 
    f number :=0;  
    CURSOR cur_b is 
     SELECT col1||col2||col3 as main_col, 
       band, 
       effective_start_date as start_date, 
       effective_end_date as end_date , 
       71 as roll_no 
    FROM schemab.tableb; 
BEGIN 
    open cur_b; 
    loop 
     fetch curb_b bulk collect into nt_a limit 1000; 
     exit when nt_a.count() = 0; 

     FORALL i IN rA.FIRST..rE.LAST 
      insert into SchemaA.TableA(main_col, BAND, user_type, START_DATE, END_DATE, roll_no) 
      values nt_a(i); 

     f := f + sql%rowcount;  
     if (f > = 10000) then 
      commit; 
      f := 0; 
     end if; 
    end loop; 
    commit; 
    close cur_b; 
end; 

ループ内でコミットを発行することは禁忌です。 ORA-01002やORA-01555などのランタイム・エラーに自分自身を晒します。あなたのプログラムが途中でクラッシュすると、問題なく再開するのが非常に難しくなります。 UNDO表領域に問題がある場合は、すべての手段が維持されますが、正しい答えはDBAにUNDO表領域を拡大してコードを弱めることではありません。

"i am using bulk insert because it gives better performance"

BULK COLLECTFORALL ... INSERTが行ごとの単一のインサートとCURSOR FORループよりも遂行であることは事実です。純粋なSQL INSERT INTO ... SELECTより効率的ではありません。構造体の価値は、配列を挿入する前に配列の内容を操作できるということです。これは、プログラム的にしか適用できない複雑なビジネスルールがある場合には役に立ちます。