暂无图片
暂无图片
5
暂无图片
暂无图片
暂无图片

震惊一个简单的sql执行十天还没执行完!!!

原创 杜伟 2024-04-12
432

周五的下午总是过的有点漫长,伴随着写周报周计划啥的,还是很慢。看看微信群里的吹水,不能卷。

划水看看某库的top sql,只是随便瞅一眼,这sql挺简单的,但是怎么执行这么慢,查看下 sql monitor,震惊体,居然从4月2日8点开始执行的,

今天是4月12日,哇。。。十天了,看来最近水划的有点严重了。

我在本地准备了测试表来展示这次案例:

--创建测试表create table dwtest1_1 as select * from dba_objects;
create table dwtest1_2 as select * from dba_objects;
create table dwtest2_1as select * from dba_objects;

收集统计信息

exec dbms_stats.gather_table_stats(ownname => 'SCOTT',tabname => 'DWTEST1_1);
exec dbms_stats.gather_table_stats(ownname => 'SCOTT',tabname => 'DWTEST1_2);
exec dbms_stats.gather_table_stats(ownname => 'SCOTT',tabname => 'DWTEST2_1);

生产上的类似sql

select count(1)
  from dwtest1_1 a, dwtest1_2 b, dwtest2_1 c
 where a.object_id = B.object_id
   and b.object_name = c.object_name
   and a.OWNER != :1
   AND B.STATUS != :2
    OR C.OWNER = :3;

F5查看下执行计划

 Plan Hash Value  : 2860411116 

---------------------------------------------------------------------------------------------------------------
| Id   | Operation                 | Name      | Rows           | Bytes            | Cost         | Time      |
---------------------------------------------------------------------------------------------------------------
|    0 | SELECT STATEMENT          |           |              1 |               79 | 107606009778 | 999:59:59 |
|    1 |   SORT AGGREGATE          |           |              1 |               79 |              |           |
|    2 |    CONCATENATION          |           |                |                  |              |           |
|    3 |     MERGE JOIN CARTESIAN  |           | 27049976911056 | 2136948175973424 | 107606008466 | 999:59:59 |
|    4 |      MERGE JOIN CARTESIAN |           |      312405896 |      21243600928 |      1245612 |  04:09:08 |
|  * 5 |       TABLE ACCESS FULL   | DWTEST2_1 |           3608 |           111848 |          346 |  00:00:05 |
|    6 |       BUFFER SORT         |           |          86587 |          3203719 |      1245267 |  04:09:04 |
|    7 |        TABLE ACCESS FULL  | DWTEST1_2 |          86587 |          3203719 |          345 |  00:00:05 |
|    8 |      BUFFER SORT          |           |          86586 |           952446 | 107606008120 | 999:59:59 |
|    9 |       TABLE ACCESS FULL   | DWTEST1_1 |          86586 |           952446 |          344 |  00:00:05 |
| * 10 |     HASH JOIN             |           |          69641 |          5501639 |         1312 |  00:00:16 |
| * 11 |      TABLE ACCESS FULL    | DWTEST1_1 |          82978 |           912758 |          346 |  00:00:05 |
| * 12 |      HASH JOIN            |           |          69642 |          4735656 |          965 |  00:00:12 |
| * 13 |       TABLE ACCESS FULL   | DWTEST1_2 |          43294 |          1601878 |          347 |  00:00:05 |
| * 14 |       TABLE ACCESS FULL   | DWTEST2_1 |          82984 |          2572504 |          346 |  00:00:05 |
---------------------------------------------------------------------------------------------------------------

预估行数 27049976911056,cost 107606009778,27万亿行,这是应用需要的数据?估计他是想得到表关联并且 a.OWNER != :1 AND B.STATUS != :2 OR C.OWNER = :3的数据。这样的写法结果也不对,必须要改写sql才能实现了,而为什么会有这样的执行计划啊,or的时候数据库优化器会尝试改写,本例中是把a.object_id = B.object_id and b.object_name = c.object_name and a.OWNER != :1 AND B.STATUS != :2这部分作为一部分的条件来执行,这里没有问题,而or后面的C.OWNER = :3作为一部分来执行时,没有关联条件了,就产生了笛卡尔连接了。


接下来尝试改写,根据上面的思路,可以补上表关联条件:

select count(1)
  from dwtest1_1 a, dwtest1_2 b, dwtest2_1 c
 where a.object_id = B.object_id
   and b.object_name = c.object_name
   and a.OWNER != :1
   AND B.STATUS != :2
    OR (C.OWNER = :3 and a.object_id = B.object_id and
       b.object_name = c.object_name);

其执行计划如下:

 Plan Hash Value  : 3449380611 

---------------------------------------------------------------------------------
| Id  | Operation              | Name      | Rows   | Bytes   | Cost | Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |           |      1 |      79 | 1420 | 00:00:18 |
|   1 |   SORT AGGREGATE       |           |      1 |      79 |      |          |
| * 2 |    HASH JOIN           |           |  72435 | 5722365 | 1420 | 00:00:18 |
|   3 |     TABLE ACCESS FULL  | DWTEST1_1 |  86586 |  952446 |  346 | 00:00:05 |
| * 4 |     HASH JOIN          |           | 144621 | 9834228 | 1072 | 00:00:13 |
|   5 |      TABLE ACCESS FULL | DWTEST2_1 |  86592 | 2684352 |  346 | 00:00:05 |
|   6 |      TABLE ACCESS FULL | DWTEST1_2 |  86587 | 3203719 |  347 | 00:00:05 |
---------------------------------------------------------------------------------

以内连接的改写方式:

select count(1)
  from dwtest1_1 a
 inner join dwtest1_2 b
    on a.object_id = B.object_id
 inner join dwtest2_1 c
    on b.object_name = c.object_name
 where a.OWNER != :1
   AND B.STATUS != :2
    OR C.OWNER = :3;

其执行计划如下:

 Plan Hash Value  : 3449380611 

---------------------------------------------------------------------------------
| Id  | Operation              | Name      | Rows   | Bytes   | Cost | Time     |
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |           |      1 |      79 | 1420 | 00:00:18 |
|   1 |   SORT AGGREGATE       |           |      1 |      79 |      |          |
| * 2 |    HASH JOIN           |           |  72435 | 5722365 | 1420 | 00:00:18 |
|   3 |     TABLE ACCESS FULL  | DWTEST1_1 |  86586 |  952446 |  346 | 00:00:05 |
| * 4 |     HASH JOIN          |           | 144621 | 9834228 | 1072 | 00:00:13 |
|   5 |      TABLE ACCESS FULL | DWTEST2_1 |  86592 | 2684352 |  346 | 00:00:05 |
|   6 |      TABLE ACCESS FULL | DWTEST1_2 |  86587 | 3203719 |  347 | 00:00:05 |
---------------------------------------------------------------------------------

两个改写方式的Plan Hash Value 都为3449380611,执行计划一致,带入了变量执行结果也是一致的。


给自己的总结:关于or的改写最近遇到了好多,应用开始很多时候写sql都是根据自己的逻辑来写代码而不知道数据库内部的执行方式,导致了慢sql,而本次生产上的sql幸好没有并发,不然那就上质量分析会了!这个sql是我遇到最慢的sql了,特来记录下。遇到or更多的时候是产生FILTER连接,通过LNNVL + union all 改写,而有时候会因为 or 是子查询方式,就需要用逻辑方式来评估了,分析其可能出现的情况来 union all了。


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

评论