Oracle update 关联更新优化方法

关联更新顾名思义就是指,更新的数据从关联的表中获取并update到目标表。并且该SQL将会是一个天然的嵌套循环。有两种优化思路解决:

1、PLSQL 根据rowid更新

是否需要加order by rowid的考量:

如果buffer cache足够大,能够放得下要被更新的表,就不需要order by rowid,因为这个过程只需要将这张表读一次进buffer cache就可以了。

如果buffer cache不够大,就需要order by rowid了。因为假如由于buffer cache不够了,导致只能page部分该表的数据到磁盘,但可能块上部分都没有更新完,就又要读回去,这样一来一回甚至需要读到内存的量远大于该表的大小;如果加了rowid排序,即时被刷出去了,也能按顺序读回去。

2、merge into

更推荐merge into,因为他不仅是多块读,而且也可以做到并行更新,缺点是消耗undo多,如果更新过程中down机了,死事务恢复会很慢。

下面通过实验来举例优化:

create table a as select * from dba_objects;

create table b as select * from dba_objects;

insert into b select * from b; ----插入到60w左右数据

----实验数据构造----

SQL> create table a as select * from dba_objects;

表已创建。

SQL> create table b as select * from dba_objects;

表已创建。

SQL> insert into b select * from b;

已创建 86081 行。

SQL> /

已创建 172162 行。

SQL> /

已创建 344324 行。

SQL> select count(*) from b;

COUNT(*)


688648

现在假设有如下关联更新语句:

update b set b.object_name=(select a.object_name from a where a.object_id=b.object_id);

SQL> explain plan for update b set b.object_name=(select a.object_name from a where a.object_id=b.object_id);

已解释。

SQL> set lines 300 pages 300

SQL> select * from table(dbms_xplan.display);

可以看到id=3有:B1,这个情况之前文章也说过了,SQL语句中本身没绑定变量,但是执行计划中出现了,说明被驱动表已经被干了n次了。

------merge into优化,使用merge into一定要注意确保merge into的表走全表扫描

alter session set db_file_multiblock_read_count=128;

如果更新的表很大可以开启并行,手动设置sort区和hash区:

alter session set enable parallel dml;

alter session set workarea_size_policy=manual;

alter session set sort_area_size=xxx;

alter session set hash_area_size=xxx;

下面是使用merge into优化该关联更新语句:

merge /*+ use_hash(a,b) parallel(a,4) paraller(b,4)*/ into b using a on (a.object_id=b.object_id) when matched then update set b.object_name=a.object_name;

09:22:42 SQL> merge /*+ use_hash(a,b) parallel(a,4) paraller(b,4)*/ into b using a on (a.object_id=b.object_id) when matched then update set b.object_name=a.object_name;

688640 行已合并。

09:22:54 SQL>

实测用了12s更新完成原先跑不完的update SQL。

------PLSQL,根据rowid优化

DECLARE

CURSOR cur_b IS

SELECT

a.object_id,

a.object_name,

b.rowid row_id

FROM

a,

b

WHERE

a.object_id = b.object_id

ORDER BY

b.rowid;

v_counter NUMBER;

BEGIN

v_counter := 0;

FOR row_b IN cur_b LOOP

UPDATE b

SET

object_name = row_b.object_name

WHERE

ROWID = row_b.row_id;

v_counter := v_counter + 1;

IF ( v_counter >= 10000 ) THEN

COMMIT;

dbms_output.put_line('Update:'

|| v_counter

|| 'lines.');

v_counter := 0;

END IF;

END LOOP;

COMMIT;

END;

/

实测用了15s更新完成

------PLSQL,根据rowid优化,并且批量游标处理

DECLARE

maxrows NUMBER DEFAULT 100000;

row_id_table dbms_sql.urowid_table;

--currecount_table dbms_sql.number_Table;

object_name_table dbms_sql.varchar2_table;

CURSOR cur_b IS

SELECT /*use_hash(a,b)*/

a.object_name,

b.rowid row_id

FROM

a,

b

WHERE

a.object_id = b.object_id

ORDER BY

b.rowid;

v_counter NUMBER;

BEGIN

v_counter := 0;

OPEN cur_b;

LOOP

EXIT WHEN cur_b%notfound;

FETCH cur_b BULK COLLECT INTO

object_name_table,

row_id_table

LIMIT maxrows;

FORALL i IN 1..row_id_table.count

UPDATE b

SET

object_name = object_name_table(i)

WHERE

ROWID = row_id_table(i);

COMMIT;

END LOOP;

END;

/

实测10s更新完成

以上就是关于对关联更新优化的几种方法,最推荐使用merge into的方法,当数据量不是特别大时,也可以用批量游标的方法去更新。

相关推荐
l1t几秒前
DeepSeek总结的PostgreSQL 19回退会一直继续,直到士气好转
数据库·postgresql
ao-weilai5 分钟前
MySQL数据库:C语言连接MySQL教程
c语言·数据库·mysql
盛世宏博智慧档案7 分钟前
档案库房温湿度采集数据存储方案:时序数据库选型与存储优化
数据库·时序数据库
禾小西7 分钟前
08丨Redis 哨兵集群:哨兵故障后还能完成切换吗?
数据库·redis·缓存
SatanII13 分钟前
MySQL 数据库学习总结|理论 + SQL 实训完整笔记
数据库·学习·mysql
oradh14 分钟前
Oracle 索引深度解析(从内部原理到实战案例)
数据库·oracle
.冰块.16 分钟前
MySQL下篇 运维实战:备份恢复 + 主从复制 + MHA 高可用,含完整拓扑
运维·mysql·读写分离·高可用·主从复制·mha
Yyyyyy~30 分钟前
[Mysql] 数据类型
数据库·mysql
FITA阿泽要努力30 分钟前
第 1 周·第 3 讲|工具如何交给模型:工具定义、参数与结构化调用
服务器·数据库·python·agent
需要82636 分钟前
缓存与数据库一致性:延迟双删与订阅 binlog 的取舍
数据库·缓存