2017-05-30 145 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

以下腳本爲我工作,我能夠在15分鐘內加載大約5百萬的數據。

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到付

+0

而不是聲明SchemaA.TableA.main_col%TYPE,我也試着給varchar2(10),日期等作爲數據類型,但它仍然給出了相同的錯誤。 – Nik

4

爲什麼不是一個簡單的

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條件:相反,測試fetch是否返回任何內容並在數組記錄爲零時退出。

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 ... INSERTCURSOR FOR環與行接一行單個插入的詳細表演。它不是純粹的SQL INSERT INTO ... SELECT。該構造的價值在於,它允許我們在插入之前操作數組的內容。如果我們有複雜的業務規則,只能以編程方式應用,這就是句柄。