2017-05-30 150 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。该构造的价值在于,它允许我们在插入之前操作数组的内容。如果我们有复杂的业务规则,只能以编程方式应用,这就是句柄。