暂无图片
暂无图片
6
暂无图片
暂无图片
暂无图片

关于一次子查询方式的UPDATE

原创 杜伟 2023-11-06
711

1、什么是标量子查询

在Oracle中,标量子查询是指返回单个值的子查询。它可以嵌套在SELECT语句的列表中,作为一个列的值返回。标量子查询经常用于计算和检索单个值,然后将其用于其他查询或操作中的条件。

示例:

create table dw1106 as select * from dba_objects where rownum <10000;
create table dw1106a as select * from dba_objects where rownum <1000;
update dw1106 set edition_name=object_name;
commit;select a.object_name,(select object_name from dw1106a  b where a.object_id=b.object_id) as new from dw1106 a where a.object_id<5000;  其中(select object_name from dw1106a  b where a.object_id=b.object_id)就是标量子查询部分,在满足a.object_id<5000中的每一行数据中,都需要对(select object_name from dw1106a  b where a.object_id=b.object_id)标量子查询进行扫描运算如循环运算,在满足a.object_id<5000;所有行中,每一个值都要进行(select object_name from dw1106a  b where b.object_id= :x),如果b.object_id= x在子查询没有值的时候,返回为空值而且子查询部分必须是唯一一个值,多值时,在扫描到该行时则会报错 ORA 01427 单行子查询返回多个行

查看标量子查询的真实的执行计划


可以看到DW1106A 被执行了4997次,因为select count(1) from dw1106 a where a.object_id<5000; a.object_id<5000对应的行数就是4997;

这样的标量子查询消耗大量逻辑读,多次对子查询部分进行扫描,在不改写语句的情况下,如果关联字段适合创建索引的话,可以创建索引。

create index idex1 on dw1106(object_id);
create index idex2 on dw1106a(object_id);

此时查看标量子查询的真实的执行计划:


标量子查询部分还是被执行4997次,但是其扫描方式是索引范围扫描了,主表这次还是全表扫描,是因为object_id<5000扫描的数量太多,全表的一半,走索引还不如全表扫描(多块读)效率高。

走索引后逻辑读大大降低了。


2、left join 改写优化标量子查询

可以将上述标量子查询修改下

select  a.object_name,b.object_name as new from dw1106 a left join dw1106a b on a.object_id=b.object_id where a.object_id<5000;

其执行计划如下:


对两个表都是扫描了一次,相比第一次的语句逻辑读降低很多,如果两个表都很大时,而且关联字段没有索引是,多次对子查询部分扫描将非常耗时。

所以建议在存在标量子查询是建议对其进行left join改写。


3、关于update的标量子查询类似的更新(一般update不称为标量子查询)

生产上类似如下这样的语句:

update dw1106 a set a.edition_name=(select b.object_name from dw1106a b where a.object_id=b.object_id) where a.object_id<5000;

其中a.edition_name=(select b.object_name from dw1106a b where a.object_id=b.object_id) 也是相当于select时候的标量子查询,在

where a.object_id<5000满足多少行时,子查询部分就需要执行多少次。

那么我们将该更新方式通过merge Into的方式改写下:

MERGE INTO dw1106 a USING dw1106a b ON (a.object_id=b.object_id)
WHEN MATCHED THEN UPDATE SET a.edition_name=b.object_name where a.object_id<5000;

可能有什么问题呢?

!!!

注意此时,如果b.object_id= x在子查询没有值的时候,返回为空值,但是merge因为务必是条件满足时才更新操作,所以merger对不满足a.object_id=b.object_id时

是不更新的。

而,update dw1106 a set a.edition_name=(select b.object_name from dw1106a b where a.object_id=b.object_id) where a.object_id<5000;这语句是会更新的为空的。

一切改写都是要满足不改变其原有结果集的,所以上面的改写方式存在问题 。

而且merge into 不支持 WHEN not MATCHED THEN UPDATE SET的方式(我这测试没有通过,万能网友是否有其他的merge的对不满足情况下还能更新吗)。

此时建议:1、咨询业务情况,是否有关联条件不匹配的情况,如果没有,则按上述方式改写。

                 2、其他方式改写,针对此例我尝试了下这样的方式,对主表进行两次全表扫描,对子查询表一次全表扫描,其意义就是用关联将object_id,空值也关联出来,再进行merge

                        更新。

MERGE INTO dw1106 a USING (select d.object_name,c.object_id as object_id from dw1106 c left join dw1106a d on c.object_id=d.object_id where c.object_id<5000)
 b ON (a.object_id=b.object_id ) 
WHEN MATCHED THEN UPDATE SET a.edition_name=b.object_name  where a.object_id<5000; Plan Hash Value  : 4089592994 

---------------------------------------------------------------------------------
| Id  | Operation                  | Name    | Rows | Bytes   | Cost | Time     |
---------------------------------------------------------------------------------
|   0 | MERGE STATEMENT            |         | 5848 | 1672528 |  112 | 00:00:02 |
|   1 |   MERGE                    | DW1106  |      |         |      |          |
|   2 |    VIEW                    |         |      |         |      |          |
| * 3 |     HASH JOIN              |         | 5848 | 1818728 |  112 | 00:00:02 |
| * 4 |      HASH JOIN RIGHT OUTER |         | 5848 |  538016 |   59 | 00:00:01 |
| * 5 |       TABLE ACCESS FULL    | DW1106A |  999 |   78921 |    6 | 00:00:01 |
| * 6 |       TABLE ACCESS FULL    | DW1106  | 5848 |   76024 |   53 | 00:00:01 |
| * 7 |      TABLE ACCESS FULL     | DW1106  | 5848 | 1280712 |   53 | 00:00:01 |


4、总结

        4.1、遇到标量子查询都需要多注意,尝试改写

        4.2、针对改写后的结果集与改写前进行比较,避免极端情况

        4.3、理解标量子查询的运算方法,万变不离其宗





最后修改时间:2024-03-27 17:12:29
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论