我有大约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;
你能帮我找一下错误所在吗?
答案 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万条记录加载到您的收藏中。这将会破坏你的会话内存限制。
使用TableB
和BULK 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