过程插入更新的3种方法:

a、逐条检查插入或更新,同时执行插入或更新

b、逐条merge into(逐条是为了记录过程日志与错误信息)

c、基于关联数组的检查插入、更新,通过forall批量sql执行

以下为模拟步骤:

1、创建模拟大表,数据量1亿不分区

create table big_table as
SELECT ROWNUM RN,'A' A,'B' B,'C' C,'D' D FROM
( SELECT ROWNUM RN FROM DUAL CONNECT BY ROWNUM <= 1000) T1,
( SELECT ROWNUM RN FROM DUAL CONNECT BY ROWNUM <= 1000) T2,
( SELECT ROWNUM RN FROM DUAL CONNECT BY ROWNUM <= 100) T3;

说明:用connect by方法创建测试大表相对较快

2、给大表加上一个主键,因为插入更新基于主键

 alter table BIG_TABLE2
add constraint PK_RN2 primary key (RN)

3、创建模拟的更新表,数据量20万

CREATE TABLE UP_TABLE ( RN NUMBER,A VARCHAR2(20),B VARCHAR2(20),C VARCHAR2(20),D VARCHAR2(20))  

4、给更新表加数据,10万更新,10万插入,写的相对烦人些。。。代码不贴出来了

5、日志表结构

6、第一种方法的过程如下:

create or replace procedure sp_ins_up is
v_bz char(1);--插入更新标志
v_stime pls_integer;--过程起始时间
i pls_integer := 0; --执行数量
v_up pls_integer := 0;--更新数量
v_ins pls_integer := 0;--插入数量
v_time pls_integer;--循环当前循环起始时间
begin
v_stime := dbms_utility.get_time();--过程起始时间
v_time := dbms_utility.get_time();--当前循环起始时间
for rec in (select * from up_table) loop--开始循环
i := i + 1;
begin--当前数据执行插入更行判断
select '' into v_bz from big_table1 a where a.rn = rec.rn;
exception
when no_data_found then
v_bz := 0;
end;
if v_bz = 1 then--更新
v_up := v_up + 1;
update big_table1 a
set a.a = rec.a, a.b = rec.b, a.c = rec.c, a.d = rec.d
where a.rn = rec.rn;
else--插入
v_ins := v_ins + 1;
insert into big_table1 values (rec.rn, rec.a, rec.b, rec.c, rec.d);
end if;
if mod(i, 5000) = 0 then--一定数量提交
insert into INS_UP_LOG
values
('sp_ins_up',
round((dbms_utility.get_time - v_time) / 100, 2),
v_up + v_ins,
v_up,
v_ins);
commit;
v_up := 0;
v_ins := 0;
v_time := dbms_utility.get_time;
end if;
end loop;
commit;
insert into ins_up_log--过程总时间记录
values
('sp_ins_up',
round((dbms_utility.get_time - v_stime) / 100, 2),
'',
'',
'');
commit;
end sp_ins_up;

7、merge into方法过程如下:

create or replace procedure sp_merge is
v_stime pls_integer; --过程起始时间
i pls_integer := 0; --执行数量
v_time pls_integer; --循环当前循环起始时间
v_rn up_table.rn%type; --缓存值
v_errmsg varchar2(500); --当前错误信息
begin
v_stime := dbms_utility.get_time();
v_time := dbms_utility.get_time();
for rec in (select * from up_table) loop
i := i + 1;
v_rn := rec.rn; --缓存当前主键
merge into big_table3 t --merge into
using (select * from up_table where rn = rec.rn) a
on (t.rn = a.rn)
when matched then
update set t.a = a.a, t.b = a.b, t.c = a.c, t.d = a.d
when not matched then
insert values (a.rn, a.a, a.b, a.c, a.d);
v_errmsg := sqlerrm;
if mod(i, 5000) = 0 then
insert into ins_up_log
values
('sp_merge',
round((dbms_utility.get_time - v_time) / 100, 2),
i,
i / 2, --此处插入数量不必在意
i / 2);
commit;
v_time := dbms_utility.get_time();
end if;
end loop;
insert into ins_up_log --过程总时间记录
values
('sp_merge',
round((dbms_utility.get_time - v_stime) / 100, 2),
i,
'',
'');
commit;
exception
when others then
insert into ins_up_err values ('sp_merge', v_rn, v_errmsg);
end sp_merge;
 

8、forall方法,结合第一种方法的判断方式:

create or replace procedure sp_forall_new is
type table_type is table of up_table%rowtype index by pls_integer; --增量表缓存
tab_all table_type; --增量表全
tab_up table_type; --增量表-更新
tab_ins table_type; --增量表-插入
v_bz char(1); --存在标志
cursor cur_up is
select * from up_table; --更新表游标
v_stime pls_integer; --过程开始时间
v_time pls_integer; --每次循环开始时间
v_num_ins pls_integer := 0; --每次循环插入数量
v_num_up pls_integer := 0; --每次循环更新数量
v_num number; --数量缓存值
v_err_msg number; --forall中错误信息
begin
v_stime := dbms_utility.get_time(); --过程开始
open cur_up; --打开游标
loop
--开始循环游标
v_time := dbms_utility.get_time(); --当前循环开始时间
v_num_ins := 0; --每次循环置零
v_num_up := 0; --每次循环置零
fetch cur_up bulk collect
into tab_all limit 5000; --批量插入缓存表,每次限定5000条
exit when tab_all.count = 0; --当缓存表中无数据,退出循环
--
for i in 1 .. tab_all.count loop
--此循环功能:判断是否存在目标表中
begin
select '' into v_bz from big_table2 t where t.rn = tab_all(i).rn; --存在
exception
when no_data_found then
v_bz := ''; --不存在
end;
--
if v_bz = '' then
--存在
v_num_up := v_num_up + 1; --更新数值+1
tab_up(v_num_up) := tab_all(i); --复制到更新缓存表
else
--不存在
v_num_ins := v_num_ins + 1; --插入数值+1
tab_ins(v_num_ins) := tab_all(i); --复制到插入缓存表
end if;
--
end loop;
--
--批量SQL,且在批量执行中错误的信息记录在sql%bulk_exceptions缓存表中
if tab_up.count <> 0 then
forall i in tab_up.first .. tab_up.last save exceptions
update big_table2 t
set t.a = tab_up(i).a,
t.b = tab_up(i).b,
t.c = tab_up(i).c,
t.d = tab_up(i).d
where t.rn = tab_up(i).rn;
for i in 1 .. sql%bulk_exceptions.count loop
v_num := sql%bulk_exceptions(i).error_index;
v_err_msg := sqlerrm(-sql%bulk_exceptions(i).error_code);
insert into ins_up_err
values
('sp_forall', tab_up(v_num).rn, v_err_msg);
end loop;
commit;
end if; --更新批量结束
--批量SQL,且在批量执行中错误的信息记录在sql%bulk_exceptions缓存表中
if tab_ins.count <> 0 then
forall i in tab_ins.first .. tab_ins.last save exceptions
insert into big_table2
values
(tab_ins(i).rn,
tab_ins(i).a,
tab_ins(i).b,
tab_ins(i).c,
tab_ins(i).d);
for i in 1 .. sql%bulk_exceptions.count loop
v_num := sql%bulk_exceptions(i).error_index;
v_err_msg := sqlerrm(-sql%bulk_exceptions(i).error_code);
insert into ins_up_err
values
('sp_forall', tab_ins(v_num).rn, v_err_msg);
end loop;
commit;
end if; --插入批量结束
insert into ins_up_log --记录日志
values
('sp_forall',
round((dbms_utility.get_time - v_time) / 100, 2),
5000,
v_num_up,
v_num_ins);
commit;
--清空当前循环插入、更新缓存表数据(不清空,下次循环重复执行)
tab_up.delete;
tab_ins.delete;
end loop;
close cur_up; --关闭游标
insert into ins_up_log --过程总时间记录
values
('sp_forall',
round((dbms_utility.get_time - v_stime) / 100, 2),
'',
'',
'');
commit;
end sp_forall_new;

最后结果:同样对一亿的表插入更新20万条数据,多次执行平均时间

sp_merge 14.48秒
sp_forall          6.63秒
sp_ins_up  44.33秒

从每5000条提交一次的时间可以得出来,forall最稳定,其次merge稍有起伏,手动执行插入更新浮动最大从0.3秒到6.9秒不等。

效率方面:forall优势明显,其次merge也不差,手动插入更新最慢且不稳定

代码方面:merge into与手动插入更新 相对简洁,forall就比较复杂

扩展性:手动插入更新可以加上许多业务性功能,forall方式目前oracle也提供了相当多的函数用于数据处理,所以次之,merge into就个人而言,相对笨重一些了。

oracle基于3种方法的大数据量插入更新的更多相关文章

  1. c#几种数据库的大数据批量插入(SqlServer、Oracle、SQLite和MySql)

    这篇文章主要介绍了c#几种数据库的大数据批量插入(SqlServer.Oracle.SQLite和MySql),需要的朋友可以了解一下. 在之前只知道SqlServer支持数据批量插入,殊不知道Ora ...

  2. mysql/oracle jdbc大数据量插入优化

    10.10.6  大数据量插入优化 在很多涉及支付和金融相关的系统中,夜间会进行批处理,在批处理的一开始或最后一般需要将数据回库,因为应用和数据库通常部署在不同的服务器,而且应用所在的服务器一般也不会 ...

  3. C# & SQL Server大数据量插入方式对比

    以下内容大部分来自: http://blog.csdn.net/tjvictor/article/details/4360030 部分内容出自互联网,实验结果为亲测. 最近自己开发一个向数据库中插入大 ...

  4. C#中几种数据库的大数据批量插入

    C#语言中对SqlServer.Oracle.SQLite和MySql中的数据批量插入是支持的,不过Oracle需要使用Orace.DataAccess驱动. IProvider里有一个用于实现批量插 ...

  5. sql server 2005 大数据量插入性能对比

    sql server 2005大数据量的插入操作 第一,写个存储过程,传入参数,存储过程里面是insert操作, 第二,用System.Data.SqlClient.SqlBulkCopy实例方法, ...

  6. 使用rowid抽取数据方法以及大数据量游标卡住的应对

    平时工作的时候,经常会遇到这种事情,从一个大表A中,抽取字段a在一个相对较小B的表的数据,比如,从一个详单表中,抽取几万个用户号码的话单出来.这种时候,一般来说, 做关联查询: create tabl ...

  7. C#:几种数据库的大数据批量插入

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

  8. C#:几种数据库的大数据批量插入(转)

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

  9. C#:几种数据库的大数据批量插入 - faib

    在之前只知道SqlServer支持数据批量插入,殊不知道Oracle.SQLite和MySql也是支持的,不过Oracle需要使用Orace.DataAccess驱动,今天就贴出几种数据库的批量插入解 ...

随机推荐

  1. React 生态系统:从小白到大神

    http://mp.weixin.qq.com/s/Epx46lznpnvgrIsbmAIZBA

  2. grep递归查找子目录

    想要在各种文件里面找一个指定的文本,本来的方法太土了,在网上搜了一下,发现个好的方法,不过也有些问题.原文如下: 第一个,这个是看别人脚本的,配合find实现,-maxdepth指定深度,如果查找到底 ...

  3. Unity版本与虚拟现实头盔Deepoon大朋版本测试

    一.看这里 Unity官方与OC runtime版本兼容性说明: https://developer.oculus.com/documentation/game-engines/latest/conc ...

  4. php查询操作实现投票功能

    这篇文章主要为大家详细介绍了php查询操作实现投票功能的具体代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下   本文实例为大家分享了php查询操作实现投票功能的代码,供大家参考,具体内容如下 ...

  5. synchronized同步方法

    “非线程安全”其实会在多个线程对同一个对象中的实例变量进行并发访问的时候产生,产生的后果是脏读,也就是取到的数据是被更改过的.而“线程安全”就是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象 ...

  6. Struts2中的OGNL详解 《转》

    首先了解下OGNL的概念: OGNL是Object-Graph Navigation Language的缩写,全称为对象图导航语言,是一种功能强大的表达式语言,它通过简单一致的语法,可以任意存取对象的 ...

  7. 配置maven使用nexus

    本文简单介绍使用配置maven使用nexus仓库,在团队中使用nexus,避免每个人都从中央仓库去下载依赖,节省带宽,提高下载速度,同时也减少了中央仓库的压力 配置在maven中使用nexus很简单( ...

  8. oracle的cursor

    oracle的cursor 转自:http://www.cnblogs.com/shengtianlong/archive/2010/12/31/1922767.html 1,什么是游标? ①从表中检 ...

  9. Oracle(四):锁

    v$lock:视图列出当前系统持有的或正在申请的所有锁的情况 v$locked_object:视图列出当前系统中哪些对象正被锁定 1).查询当前数据库锁的情况,以及导致锁的sql语句: Select ...

  10. 170505、MySQL的or/in/union与索引优化

    假设订单业务表结构为: order(oid, date, uid, status, money, time, …) 其中: oid,订单ID,主键 date,下单日期,有普通索引,管理后台经常按照da ...