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种方式及速度比对的更多相关文章

  1. mybatis+oracle 批量插入,若数据库中有则做更新操作

    1.只批量插入: insert into WXPAY_ACCOUNT(id ,out_trade_no ,transaction_id)select SEQ_WXPAY_ACCOUNT.nextval ...

  2. oracle 批量更新之update case when then

      oracle 批量更新之update case when then CreationTime--2018年8月7日15点51分 Author:Marydon 1.情景描述 根据表中同一字段不同情况 ...

  3. oracle 批量更新表字段

      (一) 将数字替换成汉字 第一步,去重查询 使用distinct关键字先对该字段值进行去重查询,看共有几种情况 --查询指定区间内表停诊字段的值 SELECT DISTINCT T.CLOSE_T ...

  4. oracle 批量更新之将一个表的数据批量更新至另一个表

      oracle 批量更新之将一个表的数据批量更新至另一个表 CreationTime--2018年7月3日17点38分 Author:Marydon Oracle 将一个表的指定字段的值更新至另一个 ...

  5. oracle 批量删除表数据的4种方式

      1.情景展示 情景一: 删除PRIMARY_INDEX_TEST表中,MINDEX_ID字段为空的数据 情景二: 删除VIRTUAL_CARD_TEST表中的脏数据 2.解决方案 情景一的解决方案 ...

  6. KETTLE 更新表的两种方式-更新控件和sql更新 2种方式的实现比较

    在实际工作中,我们有可能遇见只更新不插入的情况,可以由以下2种方式去实现: 1.更新控件 如下图所示,根据id字段,更新name和cjsj时间字段 该控件不足的地方是,用来查询关键值得字段不够灵活,一 ...

  7. oracle批量更新

    oracle批量更新 学习了:http://blog.csdn.net/zkcharge/article/details/50855755 statement.addBatch(); statemen ...

  8. oracle批量新增更新数据

    本博客介绍一下Oracle批量新增数据和更新数据的sql写法,业务场景是这样的,往一张关联表里批量新增更新数据,然后,下面介绍一下批量新增和更新的写法: 批量新增数据 对于批量新增数据,介绍两种方法 ...

  9. oracle 批量插入-支持序列自增

    1.创建表.序列 -- Create table create table test_batch ( id number not null, name ), account ) ) -- Create ...

随机推荐

  1. HTML5 本地文件操作之FileSystemAPI实例(二)

    文件操作实例整理二 1.删除文件.复制文件.移动文件 //获取请求权限 window.requestFileSystem = window.requestFileSystem || window.we ...

  2. [转]用chrome模拟微信浏览器访问需要OAuth2.0网页授权的页面

    FROM : http://blog.csdn.net/gavin_luo/article/details/40620217 现在很流行微信网页小游戏,用html5制作的小游戏移过来,可以放到微信浏览 ...

  3. [转]小心PHP的类定义顺序与继承的问题

    FROM : http://www.pakey.net/blog/php-class-shunxu.html 以下代码的运行环境均为PHP5.3.11先来看一段代码 <?php class A  ...

  4. MySql排名查询

    1.新建一张成绩表 -- 新建成绩表 CREATE TABLE IF NOT EXISTS `score` ( `id` ) NOT NULL AUTO_INCREMENT, `name` ) NOT ...

  5. Threejs 开发3D地图实践总结【转】

    Threejs 开发3D地图实践总结   前段时间连续上了一个月班,加班加点完成了一个3D攻坚项目.也算是由传统web转型到webgl图形学开发中,坑不少,做了一下总结分享. 1.法向量问题 法线是垂 ...

  6. 浅谈提升C#正则表达式效率

     摘要:说到C#的Regex,谈到最多的应该就是RegexOptions.Compiled这个东西,传说中在匹配速度方面,RegexOptions.Compiled是可以提升匹配速度的,但在启动速度上 ...

  7. SpringBoot整合Quartz定时任务 的简单实例

    POM.XML文件 <!-- 定时器任务 quartz需要导入的坐标 --> <dependency> <groupId>org.quartz-scheduler& ...

  8. zTree怎么判断树有节点展开或者完全关闭的

    树节点有个open属性,引用API: 记录 treeNode 节点的 展开 / 折叠 状态.1.初始化节点数据时,如果设定 treeNode.open = true,则会直接展开此节点2.叶子节点 t ...

  9. JAVA的Split小技巧

    在日常的开发中截取字符串必不可少,但是在JAVA中的Split截取有点特点的地方是         例如:            String str=1,2,3,; 那么  str.split(&q ...

  10. capwap学习笔记——初识capwap(五)

    3. CAPWAP Binding for IEEE 802.11 ¢ CAPWAP协议本身并不包括任何指定的无线技术.它依靠绑定协议来扩展对特定无线技术的支持. ¢ RFC5416就是用来扩展CAP ...