批量插入Oracle PL / SQL

时间:2017-05-30 06:27:54

标签: oracle plsql bulk-operations

我有大约500万条记录需要从一个模式的表复制到另一个模式的表(在同一个数据库中)。我准备了一个脚本,但它给了我以下错误。

  

ORA-06502:PL / SQL:数字或值错误:批量绑定:定义中的错误

以下是我的剧本

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;

你能帮我找一下错误所在吗?

4 个答案:

答案 0 :(得分: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。

答案 1 :(得分:1)

  

ORA-06502:PL / SQL:数字或值错误:批量绑定:定义中的错误

您收到该错误是因为您在VALUES的{​​{1}}子句中有文字。 INSERT期望所有内容都绑定到数组。

你的程序确实存在很大的问题。您在FORALL子句中没有LIMIT,因此我们会尝试将BULK COLLECT中的所有500万条记录加载到您的收藏中。这将会破坏你的会话内存限制。

使用TableBBULK COLLECT的目的是咬掉更大数据集的块并批量处理它。为此你需要一个循环。循环没有FOR条件:而是测试fetch是否返回任何内容并在数组没有记录时退出。

FORALL

请注意,在循环内发布提交是禁忌的。您可以对ORA-01002和ORA-01555等运行时错误持开放态度。如果你的程序在中途崩溃,你将很难恢复它而没有问题。如果您对UNDO表空间有问题,请务必坚持,但正确的答案是让DBA扩大UNDO表空间而不会削弱您的代码。

  

"我正在使用批量插入,因为它提供了更好的性能"

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; BULK COLLECT确实比具有逐行单个插入的FORALL ... INSERT循环更具表现力。它不比纯SQL CURSOR FOR更有效。构造的值是它允许我们在插入之前操纵数组的内容。如果我们有复杂的业务规则只能以编程方式应用,那么这就是句柄。

答案 2 :(得分:1)

以下脚本为我工作,我能够在15分钟内加载大约500万个数据。

 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
/

答案 3 :(得分:0)

请在下面更改代码的前2行后尝试:

DECLARE

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

这可能是因为数据类型/长度不匹配。在声明部分,您错过了声明一个从表继承类型。

同样如上所述,提交的f逻辑不会为你带来魔力。你应该使用LIMIT和BULL COLLECT