OR EXISTS语句的优化方法

OR EXISTS语句的优化方法:

这库一直很空闲,但无意中看了一下,发现其中很多语句都很有问题,都是典型的OR问题语句,如果并发量大的话,CPU一下子就飙高了。
OR语句一直是性能杀手,当存在一两个的时候一般可以用union和union all来优化,请看以下例子。

1.在原句中使用了or语句并且or语句里面使用exists语句,这样给优化器造成了很大的迷惑。
2.从执行计划看来,貌似没有什么问题。
3.但从统计数据来看,一致读非常高,达到了4百多万次。

select count(*)
  from UNIOMS0808.SETTLEMENT s
 where (settlementStatus = 3)
   and (companyCode like '%GDSPID01687%' or exists
        (select 1
           from UNIOMS0808.PROVIDER pro
          where s.companyCode = pro.cpId
            and pro.companyId in
                (select com.companyId
                   from UNIOMS0808.COMPANY com
                  where com.companyCode like '%GDSPID01687%')));

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT ptimizer=CHOOSE (Cost=164 Card=1 Bytes=14)
   1    0   SORT (AGGREGATE)
   2    1     FILTER
   3    2       TABLE ACCESS (FULL) OF 'SETTLEMENT' (Cost=164 Card=1939 Bytes=27146)
   4    2       NESTED LOOPS (Cost=375 Card=190 Bytes=6840)
   5    4         TABLE ACCESS (FULL) OF 'PROVIDER' (Cost=20 Card=355 Bytes=4615)
   6    4         TABLE ACCESS (BY INDEX ROWID) OF 'COMPANY' (Cost=1 Card=1 Bytes=23)
   7    6           INDEX (UNIQUE SCAN) OF 'PK_COMPANY' (UNIQUE)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
    4726442  consistent gets
          0  physical reads
          0  redo size
        406  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed

优化思路:直接将or语句转换为union语句观察一下。

select count(1)
from 
(
select SETTLEMENTID
  from UNIOMS0808.SETTLEMENT s
 where (settlementStatus = 3)
   and (companyCode like '%GDSPID01687%')
union
select SETTLEMENTID
  from UNIOMS0808.SETTLEMENT s
 where (settlementStatus = 3)
   and exists(select 1
           from UNIOMS0808.PROVIDER pro, UNIOMS0808.COMPANY com
          where s.companyCode = pro.cpId
            and pro.companyId =com.companyId
            and com.companyCode like '%GDSPID01687%')
);

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT ptimizer=CHOOSE (Cost=376 Card=1)
   1    0   SORT (AGGREGATE)
   2    1     VIEW (Cost=376 Card=1199)
   3    2       SORT (UNIQUE) (Cost=376 Card=1199 Bytes=37355)
   4    3         UNION-ALL
   5    4           TABLE ACCESS (FULL) OF 'SETTLEMENT' (Cost=164 Card=994 Bytes=24850)
   6    4           HASH JOIN (Cost=199 Card=205 Bytes=12505)
   7    6             NESTED LOOPS (Cost=34 Card=14 Bytes=504)
   8    7               TABLE ACCESS (FULL) OF 'PROVIDER' (Cost=20 Card=14 Bytes=182)
   9    7               TABLE ACCESS (BY INDEX ROWID) OF 'COMPANY' (Cost=1 Card=1 Bytes=23)
  10    9                 INDEX (UNIQUE SCAN) OF 'PK_COMPANY' (UNIQUE)
  11    6             TABLE ACCESS (FULL) OF 'SETTLEMENT' (Cost=164 Card=19884 Bytes=497100)

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
       1917  consistent gets
          0  physical reads
          0  redo size
        406  bytes sent via SQL*Net to client
        503  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          1  sorts (memory)
          0  sorts (disk)
          1  rows processed

观察效果:效果非常明显,一致读下降到2千个,是原来的千分之0.4。从执行计划中看最大不同在于新的执行计划使用了hash join,而且显示的cost比原来的还要大。
问题分析:由于or exists语句导致优化器选择了nested loops进行表连接,而nested loops对于少数据量处理是很好的,但对于全表扫描来说则效率更低,最终导致大量的一致读产生。
有兴趣的可以进一步使用10046事件进行分析,确定问题的症结。

以下提供可重现的测试方法

create table test as select * from dba_objects;
create table test1 as select * from dba_objects;

优化前
select count(*)
  from test s
 where (OBJECT_TYPE = 'VIEW')
   and (OBJECT_NAME like '%DBA_TABLES%' or exists
        (select 1
           from test1 pro
          where s.object_id = pro.object_id
            and pro.OBJECT_NAME = 'DBA_TABLES'));
            
优化后
select count(1)
from 
(
select object_id
  from test s
 where (OBJECT_TYPE = 'VIEW')
   and (OBJECT_NAME like '%DBA_TABLES%')
UNION   
select object_id
  from test s
 where (OBJECT_TYPE = 'VIEW')
   and exists
        (select 1
           from test1 pro
          where s.object_id = pro.object_id
            and pro.OBJECT_NAME = 'DBA_TABLES')
);            

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13605188/viewspace-706829/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/13605188/viewspace-706829/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值