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. [PHP] 6种负载均衡算法

    CP from  : https://www.cnblogs.com/SmartLee/p/5161415.html http://www.dataguru.cn/thread-559329-1-1. ...

  2. .Net Core配置文件读取整理

    一 .配置文件说明 1.配置,主要是 指在程序中使用的一些特殊参数,并且大多数 仅在程序启动的之后指定不需要修改. 2.在以前.Net项目中配置文件主要指app.config或web.config,但 ...

  3. Java的并发编程中的多线程问题到底是怎么回事儿?

    在我之前的一篇<再有人问你Java内存模型是什么,就把这篇文章发给他.>文章中,介绍了Java内存模型,通过这篇文章,大家应该都知道了Java内存模型的概念以及作用,这篇文章中谈到,在Ja ...

  4. Linux命令之du

    Linux du命令用来查看文件或目录所占用磁盘空间的大小(Summarize disk usage of each FILE, recursively for directories.) 语法: d ...

  5. matlib常用知识

    把文件装入矩阵 x = load('ex4x.dat'); y = load('ex4y.dat'); [m, n] = size(x); %得到矩阵x的行数和列数 ex4x.dat共80行,2列,通 ...

  6. 正向代理与反向代理区别图解 (nginx)

    1. 背景 经常听到代理,比如通常我们要上国外的网站时,需要买vpn作为跳板机器进行访问. 但是在公司里面也听到了nginx支持反向代理. 那什么是正向代理,什么是反向代理?在网上看了写内容,说一下自 ...

  7. 一步一步学android控件(之六) —— MultiAutoCompleteTextView

    今天学习的控件是MultiAutoCompleteTextView . 提到MultiAutoCompleteTextView 我们就自然而然地想到AutoCompleteTextView ,就想知道 ...

  8. Binary Tree Level Order Traversal II leetcode java

    题目: Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from ...

  9. ARouter 路由 组件 跳转 MD

    目录 简介 支持的功能 典型应用 简单使用 进阶使用 更多功能 其他 Q&A Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs bai ...

  10. CSS布局中一个简单的应用BFC的例子

    什么是BFC BFC(Block Formatting Context),简单讲,它是提供了一个独立布局的环境,每个BFC都遵守同一套布局规则.例如,在同一个BFC内,盒子会一个挨着一个的排,相邻盒子 ...