oracle批量更新之使用游标进行分批次更新的5种方式及速度比对
1.情景展示
一共有22w条数据, 需要将A表的主键更新至B表的指定字段,如何快速完成更新?
2.解决方案
声明:
解决方案不只一种,该文章只介绍快速游标法及代码实现;
两张表的ID和ID_CARD字段都建立了索引。
方式一:使用隐式游标(更新一次提交1次)
--快速游标法
BEGIN
FOR TEMP_CURSOR IN (SELECT T2.ID, T2.ID_CARD
FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
WHERE T1.ID_CARD = T2.ID_CARD
AND T1.REMARK = '**市****区数据'
AND T2.REMARK = '**市****区数据') LOOP
/* LOOP循环的是TEMP_CURSOR(逐条读取TEMP_CURSOR) */
UPDATE VIRTUAL_CARD10
SET INDEX_ID = TEMP_CURSOR.ID
WHERE ID_CARD = TEMP_CURSOR.ID_CARD;
COMMIT; --提交
END LOOP;
END;
执行时间:

方式二:使用隐式游标(更新1000次提交1次)(推荐使用)
/* 使用隐式游标进行分批次更新 */
DECLARE
V_COUNT NUMBER(10);
BEGIN
/* 隐式游标 */
FOR TEMP_CURSOR IN (SELECT T2.ID, T2.ID_CARD
FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
WHERE T1.ID_CARD = T2.ID_CARD
AND T1.REMARK = '**市****区数据'
AND T2.REMARK = '**市****区数据') LOOP
/* 业务逻辑 */
UPDATE VIRTUAL_CARD10
SET INDEX_ID = TEMP_CURSOR.ID
WHERE ID_CARD = TEMP_CURSOR.ID_CARD;
/* 更新一次,+1 */
V_COUNT := V_COUNT + 1;
/* 1000条提交1次 */
IF V_COUNT >= 1000 THEN
COMMIT; --提交
V_COUNT := 0; --重置
END IF;
END LOOP;
COMMIT; -- 提交所有数据,把这个去掉,可以查看是否是自己想要的效果,再决定是否提交
END;
执行时间:

方式三:显式游标+分批次更新(1000条1提交)
/* 使用游标进行分批次更新 */
DECLARE
V_COUNT NUMBER(10);
V_INDEX_ID PRIMARY_INDEX10.ID%TYPE;
V_ID_CARD PRIMARY_INDEX10.ID_CARD%TYPE;
CURSOR TEMP_CURSOR IS
SELECT T2.ID, T2.ID_CARD
FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
WHERE T1.ID_CARD = T2.ID_CARD
AND T1.REMARK = '**市****区数据'
AND T2.REMARK = '**市****区数据';
BEGIN
OPEN TEMP_CURSOR;
LOOP
/* 取得一行游标数据并放到对应变量中 */
FETCH TEMP_CURSOR
INTO V_INDEX_ID, V_ID_CARD;
/* 如果没有数据则退出 */
EXIT WHEN TEMP_CURSOR%NOTFOUND;
/* 业务逻辑 */
UPDATE VIRTUAL_CARD10
SET INDEX_ID = V_INDEX_ID
WHERE ID_CARD = V_ID_CARD;
/* 更新一次,+1 */
V_COUNT := V_COUNT + 1;
/* 1000条提交1次 */
IF V_COUNT >= 1000 THEN
COMMIT; --提交
V_COUNT := 0; --重置
END IF;
END LOOP;
COMMIT; -- 提交所有数据,把这个去掉,可以查看是否是自己想要的效果,再决定是否提交
CLOSE TEMP_CURSOR;
END;
执行时间:

10000条1提交,执行时间:

方式四:显式游标+数组(更新一次提交一次)(使用BULK COLLECT)
/* 使用游标+数组进行更新(更新一次提交一次) */
DECLARE
/* 创建数组:一列多行 */
TYPE TYPE_INDEX_ID IS TABLE OF PRIMARY_INDEX10.ID%TYPE;
TYPE TYPE_ID_CARD IS TABLE OF PRIMARY_INDEX10.ID_CARD%TYPE;
/* 起别名 */
V_INDEX_ID TYPE_INDEX_ID;
V_ID_CARD TYPE_ID_CARD;
/* 将查询出来的数据放到游标里 */
CURSOR TEMP_CURSOR IS
SELECT T2.ID, T2.ID_CARD
FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
WHERE T1.ID_CARD = T2.ID_CARD
AND T1.REMARK = '**市****区数据'
AND T2.REMARK = '**市****区数据';
BEGIN
OPEN TEMP_CURSOR;
LOOP
/* 取得1000行游标数据并放到对应数组中,每次读取1000条数据 */
FETCH TEMP_CURSOR BULK COLLECT
INTO V_INDEX_ID, V_ID_CARD LIMIT 1000;
/* 如果没有数据则退出 */
EXIT WHEN TEMP_CURSOR%NOTFOUND;
/* 遍历数据 */
FOR I IN V_INDEX_ID.FIRST .. V_INDEX_ID.LAST LOOP
/* 业务逻辑 */
UPDATE VIRTUAL_CARD10
SET INDEX_ID = V_INDEX_ID(I)
WHERE ID_CARD = V_ID_CARD(I);
COMMIT;
END LOOP;
END LOOP;
CLOSE TEMP_CURSOR;
END;
执行时间:

方式五: 显式游标+数组(1000条提交一次)(使用BULK COLLECT)
/* 使用游标+数组进行更新(1000条提交一次) */
DECLARE
/* 创建数组:一列多行 */
TYPE TYPE_INDEX_ID IS TABLE OF PRIMARY_INDEX10.ID%TYPE;
TYPE TYPE_ID_CARD IS TABLE OF PRIMARY_INDEX10.ID_CARD%TYPE;
/* 起别名 */
V_INDEX_ID TYPE_INDEX_ID;
V_ID_CARD TYPE_ID_CARD;
/* 将查询出来的数据放到游标里 */
CURSOR TEMP_CURSOR IS
SELECT T2.ID, T2.ID_CARD
FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
WHERE T1.ID_CARD = T2.ID_CARD
AND T1.REMARK = '**市****区数据'
AND T2.REMARK = '**市****区数据';
BEGIN
OPEN TEMP_CURSOR;
LOOP
/* 取得1000行游标数据并放到对应数组中 */
FETCH TEMP_CURSOR BULK COLLECT
INTO V_INDEX_ID, V_ID_CARD LIMIT 1000;
/* 如果没有数据则退出 */
EXIT WHEN TEMP_CURSOR%NOTFOUND;
/* 遍历数据 */
FOR I IN V_INDEX_ID.FIRST .. V_INDEX_ID.LAST LOOP --或者:FOR I IN 1 .. V_INDEX_ID.COUNT LOOP
/* 业务逻辑 */
UPDATE VIRTUAL_CARD10
SET INDEX_ID = V_INDEX_ID(I)
WHERE ID_CARD = V_ID_CARD(I);
IF I >= V_INDEX_ID.LAST THEN
COMMIT; --提交
END IF;
END LOOP;
END LOOP;
CLOSE TEMP_CURSOR;
END;
执行时间:

方式六:推荐使用(使用BULK COLLECT和FORALL)
/* 使用游标+数组进行更新(BULK COLLECT和FORALL) */
DECLARE
/* 创建数组:一列多行 */
TYPE TYPE_INDEX_ID IS TABLE OF PRIMARY_INDEX10.ID%TYPE;
TYPE TYPE_ID_CARD IS TABLE OF PRIMARY_INDEX10.ID_CARD%TYPE;
/* 起别名 */
V_INDEX_ID TYPE_INDEX_ID;
V_ID_CARD TYPE_ID_CARD;
/* 将查询出来的数据放到游标里 */
CURSOR TEMP_CURSOR IS
SELECT T2.ID, T2.ID_CARD
FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
WHERE T1.ID_CARD = T2.ID_CARD
AND T1.REMARK = '**市****区数据'
AND T2.REMARK = '**市****区数据';
BEGIN
OPEN TEMP_CURSOR;
LOOP
/* 取得1000行游标数据并放到对应数组中 */
FETCH TEMP_CURSOR BULK COLLECT
INTO V_INDEX_ID, V_ID_CARD LIMIT 1000;
/* 如果没有数据则退出 */
EXIT WHEN TEMP_CURSOR%NOTFOUND;
/* 遍历数据 */
FORALL I IN 1 .. V_INDEX_ID.COUNT-- 或者V_INDEX_ID.FIRST .. V_INDEX_ID.LAST
/* 业务逻辑 */
UPDATE VIRTUAL_CARD10
SET INDEX_ID = V_INDEX_ID(I)
WHERE ID_CARD = V_ID_CARD(I);
COMMIT; --提交
END LOOP;
CLOSE TEMP_CURSOR;
END;
执行时间:

从Oracle8开始,oracle为PL/SQL引入了两个新的数据操纵语言(DML)语句:BULK COLLECT和FORALL。
这两个语句在PL/SQL内部进行一种数组处理;BULK COLLECT提供对数据的高速检索,FORALL可大大改进INSERT、UPDATE和DELETE操作的性能。
Oracle数据库使用这些语句大大减少了PL/SQL与SQL语句执行引擎的环境切换次数,从而使其性能有了显著提高。
小结:
数据量小的时候可以用方式二,数据量大的时候推荐使用方式六;
一定要建索引。
写在最后
哪位大佬如若发现文章存在纰漏之处或需要补充更多内容,欢迎留言!!!
相关推荐:
oracle批量更新之使用游标进行分批次更新的5种方式及速度比对的更多相关文章
- mybatis+oracle 批量插入,若数据库中有则做更新操作
1.只批量插入: insert into WXPAY_ACCOUNT(id ,out_trade_no ,transaction_id)select SEQ_WXPAY_ACCOUNT.nextval ...
- oracle 批量更新之update case when then
oracle 批量更新之update case when then CreationTime--2018年8月7日15点51分 Author:Marydon 1.情景描述 根据表中同一字段不同情况 ...
- oracle 批量更新表字段
(一) 将数字替换成汉字 第一步,去重查询 使用distinct关键字先对该字段值进行去重查询,看共有几种情况 --查询指定区间内表停诊字段的值 SELECT DISTINCT T.CLOSE_T ...
- oracle 批量更新之将一个表的数据批量更新至另一个表
oracle 批量更新之将一个表的数据批量更新至另一个表 CreationTime--2018年7月3日17点38分 Author:Marydon Oracle 将一个表的指定字段的值更新至另一个 ...
- oracle 批量删除表数据的4种方式
1.情景展示 情景一: 删除PRIMARY_INDEX_TEST表中,MINDEX_ID字段为空的数据 情景二: 删除VIRTUAL_CARD_TEST表中的脏数据 2.解决方案 情景一的解决方案 ...
- KETTLE 更新表的两种方式-更新控件和sql更新 2种方式的实现比较
在实际工作中,我们有可能遇见只更新不插入的情况,可以由以下2种方式去实现: 1.更新控件 如下图所示,根据id字段,更新name和cjsj时间字段 该控件不足的地方是,用来查询关键值得字段不够灵活,一 ...
- oracle批量更新
oracle批量更新 学习了:http://blog.csdn.net/zkcharge/article/details/50855755 statement.addBatch(); statemen ...
- oracle批量新增更新数据
本博客介绍一下Oracle批量新增数据和更新数据的sql写法,业务场景是这样的,往一张关联表里批量新增更新数据,然后,下面介绍一下批量新增和更新的写法: 批量新增数据 对于批量新增数据,介绍两种方法 ...
- oracle 批量插入-支持序列自增
1.创建表.序列 -- Create table create table test_batch ( id number not null, name ), account ) ) -- Create ...
随机推荐
- window.opener()方法
<!DOCTYPE html><html><head><meta charset="GBK"><title>菜鸟教程(r ...
- Kubernetes中Pod的健康检查
本文介绍 Pod 中容器健康检查相关的内容.配置方法以及实验测试,实验环境为 Kubernetes 1.11,搭建方法参考kubeadm安装kubernetes V1.11.1 集群 0. 什么是 C ...
- HipHop PHP & HHVM资料收集
百度百科 HipHop PHP实战(详解web运行模式) 百度 PHP7和HHVM的性能之争
- 微信Access Token 缓存方法
微信Access Token默认缓存是2小时,但是需要特别强调,微信服务号和微信企业号缓存并不相同. (1)微信公众号号:每次Http请求Access Token 系统会返回不同的Token,并附带超 ...
- HashMap 与 ConcurrentHashMap
1. HashMap 1) 并发问题 HashMap的并发问题源于多线程访问HashMap时, 如果存在修改Map的结构的操作(增删, 不包括修改), 则有可能会发生并发问题, 表现就是get()操作 ...
- Java NIO: Non-blocking Server
Even if you understand how the Java NIO non-blocking features work (Selector, Channel, Buffer etc.), ...
- 深挖android low memory killer
对于PC来说,内存是至关重要.如果某个程序发生了内存泄漏,那么一般情况下系统就会将其进程Kill掉.Linux中使用一种名称为OOM(Out Of Memory,内存不足)的机制来完成这个任务,该机制 ...
- 【Java】Java-UTC-时间戳处理
Java-UTC-时间戳处理 java utc 时间戳_百度搜索 JAVA获取时间戳,哪个更快 - 潇湘客 - ITeye博客 Java获取UTC时间的方法详解_java_脚本之家 Java UTC时 ...
- ORACLE中union/union all/Intersect/Minus用法
Union,对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序: Union All,对两个结果集进行并集操作,包括重复行,不进行排序: Intersect,对两个结果集进行交集操作,不包 ...
- 微信小程序通过api接口将json数据展现到小程序示例
这篇文章主要介绍了微信小程序通过api接口将json数据展现到小程序示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧实现知乎客户端的一个重要知识前提就是,要知道怎么通过 ...