0

我有大约 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

4 回答 4

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永远不会是真的,因此你不会做出承诺。

于 2017-05-30T06:43:03.053 回答
2

以下脚本对我有用,我能够在 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
/
于 2017-07-31T13:02:04.133 回答
1

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

你得到这个错误是因为你VALUESINSERT. FORALL期望一切都绑定到一个数组。

从字面上看,您的程序有一个大问题。您没有LIMITBULK COLLECT子句,因此这将尝试将所有 500 万条记录加载TableB到您的集合中。这将打破您会话的内存限制。

使用BULK COLLECTandFORALL的目的是咬掉更大数据集的块并分批处理它。为此,您需要一个循环。该循环没有 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 表空间,而不是削弱你的代码。

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

确实,BULK COLLECT并且FORALL ... INSERTCURSOR FOR具有逐行单次插入的循环更具执行性。它并不比纯 SQL 更有效INSERT INTO ... SELECT。该构造的价值在于它允许我们在插入数组之前对其内容进行操作。如果我们有只能以编程方式应用的复杂业务规则,则可以处理。

于 2017-05-30T08:11:55.107 回答
0

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

DECLARE

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

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

同样如前所述,提交的 f 逻辑不会为您带来魔力。最好将 LIMIT 与 BULL COLLECT 一起使用

于 2017-05-30T06:41:16.163 回答