update merge 大表性能优化 index的作用巨大

update customers a

set (city_name, customer_type) =

(select b.city_name, b.customer_type

from tmp_cust_city b

where b.customer_id = a.customer_id)

where exists

(select /*+ use_nl(b ) */ 1 from tmp_cust_city b where b.customer_id = a.customer_id)

---这种优化,

A大表可能使用/*+ use_hash(b ) */ hash join将匹配的找出来,再根据customer_id去找b.下的记录,这样需要在b表的customer_id创建index---性能差距几百倍

如果a表很小,可以直接使用nest loop循坏更新,b表的customer_id也必须要创建index

/*+ use_nl(b ) */

近日,在项目中遇到一个需求。需要批量跑sql脚本。并且在脚本中,需要将A表中的数据根据B表的条件进行批量更新。但是,oracle中不支持update中使用left join等。

原因

Oracle没有update from语法,set前面只能存在一个表,逻辑只能在子查询中处理。

实现办法

1.子查询

写法一:

UPDATE table_1 a

SET col_x1 = (SELECT b.col_y1, b.col_y2 FROM table_2 b WHERE b.col_n = a.col_m),

col_x2 = (SELECT b.col_y2 FROM table_2 b WHERE b.col_n = a.col_m)

WHERE EXISTS (SELECT * FROM table_2 b WHERE b.col_n = a.col_m) ;

这其中table_2是同一张表,可以直接两列一起update,如果能在table_2将index 性能也可以,但是不能,这个sql性能很差

写法二:

UPDATE table_1 a

SET (col_x1, col_x2) = (SELECT b.col_y1, b.col_y2 FROM table_2 b WHERE b.col_n = a.col_m) 这个()要有

WHERE EXISTS (SELECT * FROM table_2 b WHERE b.col_n = a.col_m);

对于子查询的值只能是一个唯一值,不能是多值。

子查询在绝大多数情况下,最后面的where EXISTS子句是重要的,否则将得到错误的结果。且where EXISTS子句可用另一方法IN代替,如上。最后的子句是对a表被更新记录的限制,如无此句,对于a表中某记录,

如在b表中关联不到对应的记录,则该记录被更新字段将被更新为null。where EXISTS子句就是排除对a表中该情况的记录进行更新。

--如果A表巨大,B表也大,而且不能再B上建index 如何处理呢,可以先用with as将AB表join后的结果集取出来,再进行update,with as不能建index,所以,可以用临时表解决。

上次的例子是原来20分钟语句使用with as改写是11秒,如果再用临时表改写是0.5秒。

三张表也是同样的道理,如果A表的2 3列分别修改为B C表的第二列,也可以将ABC关联起来,类似维度表和Fact表,再将数值赋上

2.利用视图

UPDATE (SELECT A.NAME ANAME,B.NAME BNAME FROM A,B WHERE A.ID=B.ID)

SET ANAME=BNAME;

1

2

注意:

对于视图更新的限制:

如果视图基于多个表的连接,那么用户更新(update)视图记录的能力将受到限制。除非update只涉及一个表且视图列中包含了被更新的表的整个主键,否则不能更新视图的基表。

ID是A表还是B表的主键不同,可能导致修改的结果不能, 没有主键10G之后报错。

3.存储过程

MERGE INTO T2

USING T1

ON (T2.A = T1.A)

WHEN MATCHED THEN

UPDATE SET T2.C = T1.B

另外,Oracle中的Delete的from子句也没有多表级联删除的功能,只能通过子查询的方式来做:

delete from 表A where exists (select * from 表B where 表A.empid=表B.empid)

这两个开两个窗口同时改就可以修改A B表的匹配的记录

delete from 表B where 表B.empid in (select empid from 表A)

----------------------总结,

1.单条语句更新

语法:UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值

说明:如果更新的字段加了索引,更新时会重建索引,更新效率会慢。单表更新或较简单的语句采用使用此方案更优。

2.批量数据更新

语法:update 表a set a.字段1 = (select b.字段1 from 表b where a.字段2=b.字段2) where exists(select 1 from 表b where a.字段2=b.字段2)

说明:查表a的所有数据,循环每条数据,验证该条数据是否符合exists(select 1 from 表b where a.字段2=b.字段2)条件,如果是则执行(select b.字段1 from 表b where a.字段2=b.字段2)查询,查到对应的值更新a.字段1中。关联表更新时一定要有exists(select 1 from 表b where a.字段2=b.字段2)这样的条件,否则将表a的其他数据的字段1更新为null值。

3.merge更新法

语法:merge是oracle特有的语句,语法如下:

MERGE INTO table_name t1

USING (table|view|sub_query) t2

ON (join condition)

WHEN MATCHED THEN

UPDATE table_name

SET col1 = col_val1,

col2 = col2_val

WHEN NOT MATCHED THEN

INSERT (column_list) VALUES (column_values);

说明:在t2中Select出来的数据,每一条都跟t1进行 ON (join condition)的比较,如果匹配,就进行更新的操作(Update),如果不匹配,就进行插入操作(Insert)。执行merge不会返回影响的行数。Merge语句的写法比较繁琐,并且最多只能两个表关联,复杂的语句用merge更新将力不从心且效率差。两表关联且被更新表不是通过关联表主键关联的,采用此方案更优。

4.利用存储过程中游标更新

语法:

begin

for cur in (查询语句) loop ---循环

--更新语句(根据查询出来的结果集合)

end loop; --结束循环

end;

说明:oracle支持快速游标,不需要定义直接把游标写到for循环中,这样就方便了我们批量更新数据。再加上oracle的rowid物理字段(oracle默认给每个表都有rowid这个字段,并且是唯一索引),可以快速定位到要更新的记录上。使用快速游标的好处很多,可以支持复杂的查询语句,更新准确,无论数据多大更新效率仍然高,但执行后不返回影响行数。

多表关联且逻辑复杂的,采用此方案更优。

相关推荐
苏三说技术2 小时前
Redis 性能优化的18招
数据库·redis·性能优化
dingdingfish3 小时前
JSON 系列之1:将 JSON 数据存储在 Oracle 数据库中
oracle·json·database
程序猿会指北4 小时前
【鸿蒙(HarmonyOS)性能优化指南】内存分析器Allocation Profiler
性能优化·移动开发·harmonyos·openharmony·arkui·组件化·鸿蒙开发
程序猿会指北7 小时前
【鸿蒙(HarmonyOS)性能优化指南】启动分析工具Launch Profiler
c++·性能优化·harmonyos·openharmony·arkui·启动优化·鸿蒙开发
dingdingfish8 小时前
JSON 系列之2:JSON简单查询
oracle·json·database·19c·23ai
dazhong201211 小时前
PLSQL 客户端连接 Oracle 数据库配置
数据库·oracle
彭亚川Allen20 小时前
优化了2年的性能,没想到最后被数据库连接池坑了一把
数据库·后端·性能优化
MClink21 小时前
Go怎么做性能优化工具篇之pprof
开发语言·性能优化·golang
一行玩python1 天前
sqlite3,一个轻量级的 C++ 数据库库!
开发语言·数据库·c++·oracle·sqlite