目录

一、大批量数据处理... 1

1、创建表object_test和object_test1. 1

2、使用批量插入的方式插入数据... 1

3、在批处理处增加forall的异常处理... 2

4、使用普通游标进行插入数据... 4

5、总结... 6

二、commit. 7

1、每一条commit. 7

2、最后commit. 7

3、总结... 8

三、forall 与 for. 8

1、forall 8

2、for. 9

3、总结... 11

一、大批量数据处理

1、创建表object_test和object_test1

CREATE TABLE object_test AS

SELECT o.owner,o.object_name,o.last_ddl_time,o.status

FROM dba_objects o

WHERE 1>2;

CREATE TABLE object_test1 AS

SELECT o.owner,o.object_name,o.last_ddl_time,o.status

FROM dba_objects o

WHERE 1>2;

clip_image001

2、使用批量插入的方式插入数据

因为用批量插入的方法将dba_objects表中的数据插入到object_test中,只要了不到1秒的时间,所以执行了几次以后,让object_test的数据到达25万条,再插入到object_test1中。

DECLARE

CURSOR cur_objt_all IS

SELECT ot.owner,ot.object_name,ot.last_ddl_time,ot.status

FROM object_testot;

l_count NUMBER :=50000;

TYPE t_objt IS TABLE OF cur_objt_all%ROWTYPE INDEX BY BINARY_INTEGER;

v_t_objtt_objt;

BEGIN

OPEN cur_objt_all;

LOOP

FETCH cur_objt_all BULK COLLECT INTO v_t_objt LIMIT l_count;

EXIT WHEN cur_objt_all%NOTFOUND;

FORALL i IN v_t_objt.first..v_t_objt.last

INSERT INTO object_test1 VALUES v_t_objt(i);

COMMIT;

END LOOP;

COMMIT;

CLOSE cur_objt_all;

END;

1)查询表object_test中的数据

clip_image002

2)将object_test表的数据插入到object_test1表中

clip_image004

3) 按每批5万数据量进行插入提交,统共花了1.794s

3、在批处理处增加forall的异常处理

forall后只能有一条dml语句,但oracle让每执行一次这条dml语句的时候,都会设置一个savepoint,以方便出现异常状况时候的处理。如果直接处理exception,则发生异常的地方后面有数据的话,就会插入(假设dml语句做的是插入)不进去。这时,若save exceptions,则报错的地方就会回滚到上一个savepoint,即只记录此条发生的错误,然后继续运行下去。这样就会把符合约束的记录插入到表中,而报错的就会被跳过。最后,对该异常的数据,用dbms_output进行输出。

--purpose:实现大批量从一个表插入数据到另一个表中

DECLARE

CURSOR CUR_OBJT_ALL IS

SELECT OT.OWNER, OT.OBJECT_NAME, OT.LAST_DDL_TIME, OT.STATUS

FROM OBJECT_TEST OT;

L_COUNT NUMBER := 50000; --批处理的limit值

PO_ERROR_MSG VARCHAR2(500); --错误信息反馈

BULK_ERRORS EXCEPTION; --批处理异常

PRAGMA EXCEPTION_INIT(BULK_ERRORS, -24381);

TYPE T_OBJT IS TABLE OF CUR_OBJT_ALL%ROWTYPE INDEX BY BINARY_INTEGER;

V_T_OBJT T_OBJT;

BEGIN

OPEN CUR_OBJT_ALL;

LOOP

--批量插入

FETCH CUR_OBJT_ALL BULK COLLECT

INTO V_T_OBJT LIMIT L_COUNT;

EXIT WHEN CUR_OBJT_ALL%NOTFOUND;

--插入数据

BEGIN

FORALL I IN V_T_OBJT.FIRST .. V_T_OBJT.LAST SAVE EXCEPTIONS

INSERT INTO OBJECT_TEST1 VALUES V_T_OBJT (I);

--forall异常处理

EXCEPTION

WHEN BULK_ERRORS THEN

FOR N_ERROR_POINTER IN 1 .. SQL%BULK_EXCEPTIONS.COUNT LOOP

DBMS_OUTPUT.PUT_LINE('error ' || N_ERROR_POINTER ||

': The error DML number is ' ||

SQL%BULK_EXCEPTIONS(N_ERROR_POINTER)

.ERROR_INDEX);

DBMS_OUTPUT.PUT_LINE('The Oracle error is ' ||

SQLERRM(-1 *

SQL%BULK_EXCEPTIONS(N_ERROR_POINTER)

.ERROR_CODE));

END LOOP;

END;

--提交

COMMIT;

END LOOP;

--扫尾处理

COMMIT;

CLOSE CUR_OBJT_ALL;

--异常处理

EXCEPTION

WHEN OTHERS THEN

PO_ERROR_MSG := 'Others exception raise!' || SQLERRM;

DBMS_OUTPUT.PUT_LINE(PO_ERROR_MSG);

END;

clip_image005

clip_image006

结果:3.042seconds内完成插入操作

4、使用普通游标进行插入数据

1)--清空数据

TRUNCATE TABLE object_test1;

2)编写程序

考虑到批量处理的时候有5万条就提交一次,所以这里也有。

--purpose:使用普通游标实现大批量从一个表插入数据到另一个表中

--created by chenxi 2013-3-23

DECLARE

CURSOR CUR_OBJT_ALL IS

SELECT OT.OWNER, OT.OBJECT_NAME, OT.LAST_DDL_TIME, OT.STATUS

FROM OBJECT_TEST OT;

--记录类型

TYPE OBJECT_TEST_REC IS RECORD(

OWNER OBJECT_TEST.OWNER%TYPE,

OBJECT_NAME OBJECT_TEST.OBJECT_NAME%TYPE,

LAST_DDL_TIME OBJECT_TEST.LAST_DDL_TIME%TYPE,

STATUS OBJECT_TEST.STATUS%TYPE);

LV_OBJECT_TEST_REC OBJECT_TEST_REC;

LV_COUNT_COMMIT NUMBER := 50000; --插入5万条数据提交一次

LV_COUNT NUMBER := 0; --计数

BEGIN

OPEN CUR_OBJT_ALL;

LOOP

FETCH CUR_OBJT_ALL

INTO LV_OBJECT_TEST_REC;

EXIT WHEN CUR_OBJT_ALL%NOTFOUND;

INSERT INTO OBJECT_TEST1

(OWNER, OBJECT_NAME, LAST_DDL_TIME, STATUS)

VALUES

(LV_OBJECT_TEST_REC.OWNER,

LV_OBJECT_TEST_REC.OBJECT_NAME,

LV_OBJECT_TEST_REC.LAST_DDL_TIME,

LV_OBJECT_TEST_REC.STATUS);

--达到5万条的时候就提交一次

LV_COUNT := LV_COUNT + 1;

IF LV_COUNT = LV_COUNT_COMMIT THEN

COMMIT;

ELSE

NULL;

END IF;

END LOOP;

--扫尾操作

COMMIT;

CLOSE CUR_OBJT_ALL;

EXCEPTION

WHEN OTHERS THEN

DBMS_OUTPUT.PUT_LINE('Others exception raise!' || SQLERRM);

CLOSE CUR_OBJT_ALL;

END;

3)执行结果

clip_image007

clip_image008

结果:14.431seconds内完成插入操作

5、总结

由大批量插入只用了3.042seconds就完成了操作,而采用游标一条条插入就需要14.431seconds。

由上述数据,可以看出,节省的时间是相当可观的。而且oracle对大批量数据处理时出现的异常也有较完善的处理机制,允许我们在处理的时候出现部分错误,而不会影响所有数据的操作。所以,在处理大批量数据的时候,我们应该尽量使用bulk collect,forall来处理。

二、commit

1、每一条commit

1)编写程序

由于开始用了rownum对数据进行插入,直接就卡住了,就改成了下面这个简单的例子

--每一条commit

BEGIN

FOR L_COUNT IN 1 .. 100000 LOOP

INSERT INTO OBJECT_TEST1

(OWNER, OBJECT_NAME, LAST_DDL_TIME, STATUS)

Values(L_COUNT,'test',Sysdate,'test');

COMMIT;

END LOOP;

END;

2)运行结果

clip_image010

结果:在14.992s完成10万条数据的插入

2、最后commit

1)编写程序

--最后commit

BEGIN

FOR L_COUNT IN 1 .. 100000 LOOP

INSERT INTO OBJECT_TEST1

(OWNER, OBJECT_NAME, LAST_DDL_TIME, STATUS)

Values(L_COUNT,'test',Sysdate,'test');

END LOOP;

COMMIT;

END;

2)运行结果

clip_image012结果:在5.991s完成10万条数据的插入

3、总结

每一条commit结果:在14.992s完成10万条数据的插入

最后commit结果:在5.991s完成10万条数据的插入

从上述数据,感觉差异是明显的。在达到一定数值的时候就commit一下,可以避免过多暂用内存空间,以及可能丢失大量数据。

三、forall与 for

1、forall

1)清空object_test2

TRUNCATE TABLE object_test2

2)程序编写

---forall

DECLARE

CURSOR CUR_BOJECT_TEST1 IS

SELECT OWNER, OBJECT_NAME, LAST_DDL_TIME, STATUS FROM OBJECT_TEST1;

TYPE T_OBJECT_TEST1 IS TABLE OF CUR_BOJECT_TEST1%ROWTYPE INDEX BY BINARY_INTEGER;

LV_OBJECT_TEST1 T_OBJECT_TEST1;

BEGIN

OPEN CUR_BOJECT_TEST1;

FETCH CUR_BOJECT_TEST1 BULK COLLECT

INTO LV_OBJECT_TEST1;

CLOSE CUR_BOJECT_TEST1;

FORALL I IN LV_OBJECT_TEST1.FIRST .. LV_OBJECT_TEST1.LAST

INSERT INTO OBJECT_TEST2 VALUES LV_OBJECT_TEST1 (I);

COMMIT;

END;

3)运行结果

clip_image013clip_image015

结果:插入10万数据到object_test2,只用了0.296s

2、for

1)清空object_test2

TRUNCATE TABLE object_test2

2)程序编写

--for

DECLARE

CURSOR CUR_BOJECT_TEST1 IS

SELECT OWNER, OBJECT_NAME, LAST_DDL_TIME, STATUS FROM OBJECT_TEST1;

TYPE T_OBJECT_TEST1 IS TABLE OF CUR_BOJECT_TEST1%ROWTYPE INDEX BY BINARY_INTEGER;

LV_OBJECT_TEST1 T_OBJECT_TEST1;

BEGIN

OPEN CUR_BOJECT_TEST1;

FETCH CUR_BOJECT_TEST1 BULK COLLECT

INTO LV_OBJECT_TEST1;

CLOSE CUR_BOJECT_TEST1;

FOR I IN 1 .. LV_OBJECT_TEST1.COUNT LOOP

INSERT INTO OBJECT_TEST2

(OWNER, OBJECT_NAME, LAST_DDL_TIME, STATUS)

VALUES

(LV_OBJECT_TEST1(I).OWNER,

LV_OBJECT_TEST1(I).OBJECT_NAME,

LV_OBJECT_TEST1(I).LAST_DDL_TIME,

LV_OBJECT_TEST1(I).STATUS);

END LOOP;

COMMIT;

END;

3)运行结果

clip_image016

clip_image018

结果:插入10万数据到object_test2里,用了5.304s

3、总结

forall结果:插入10万数据到object_test2,只用了0.296s

for结果:插入10万数据到object_test2里,用了5.304s

在编写的过程中,尽量让其它因素都可以忽略,尽量使用大部分代码都相同,只有循环的部分是for与forall的差异。经过测试,我们可以看到在大数据量的循环中,forall有着绝对的优势。

 posted on 2013-08-12 07:46  cc11cc  阅读(623)  评论(0编辑  收藏  举报