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 ...
随机推荐
- Martin Fowler谈微服务的优缺点
很多开发团队已经认识到微服务架构比单体架构更优越.但是也有其他团队感觉到这是一种消弱生产力的负担.就像任何软件架构,微服务架构同样有利弊.为了能做出一个明智的选择,你必须了解这些应用并将它们运用到你特 ...
- FPC and Qt
Introduction There are a number of Qt bindings available: Qt3 A QtC based binding by Theo Another Qt ...
- 一个简易Asp.net网站日志系统
前不久在网站上看到了网站日志访问记录组件UserVisitLogsHelp开源了! 这篇博客感觉还不错,就把源码download了下来,学习一下,发现里面的代码书写和设计并不是很好,于是自己改了改.自 ...
- AVL树原理及实现 +B树
1. AVL定义 AVL树是一种改进版的搜索二叉树.对于一般的搜索二叉树而言,如果数据恰好是按照从小到大的顺序或者从大到小的顺序插入的,那么搜索二叉树就对退化成链表,这个时候查找,插入和删除的时间都会 ...
- centos6.8 mysql5.6.34 root密码重置
1.关闭正在运行的MySQL service mysql stop 2.启动MySQL的安全模式 mysqld_safe --skip-grant-tables 等1分钟如果还没返回的话,新开shel ...
- Mysql 高可用方案
1 mysql分布式数据库,如 mycat https://www.cnblogs.com/zzsdream/articles/6650690.html 2 读写分离,mysql主从复制+mysql ...
- Matplotlib.pyplot 常用方法
1.介绍 Matplotlib 是一个 Python 的 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形.通过 Matplotlib,开发者可以仅需要几行代码,便可以生成绘图 ...
- 解决电脑上PPT频繁刷新的问题
操作步骤: 桌面鼠标右击--->屏幕分辨率-----> 高级设置 ----->监视器----->颜色----->选择:增强色(16位)--->确定完成
- A Complete ActiveX Web Control Tutorial
A Complete ActiveX Web Control Tutorial From: https://www.codeproject.com/Articles/14533/A-Complete- ...
- C++ REST SDK i
Welcome! The C++ REST SDK is a Microsoft project for cloud-based client-server communication in nati ...