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

1. 


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这个字段,并且是唯一索引),可以快速定位到要更新的记录上。使用快速游标的好处很多,可以支持复杂的查询语句,更新准确,无论数据多大更新效率仍然高,但执行后不返回影响行数。

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


 

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值