一个非常不友好的SQL的优化改写

来源:互联网 发布:歌华有线网络 编辑:程序博客网 时间:2024/05/16 12:29

这个SQL 是在项目中遇到, 性能非常不好,  隐患非常之大。 可理解程度非常不好, 优化难度一般但真正 和业务关联后 优化程度 还可以。

另外 主要涉及 到行列转化, 和在分组数据中 提取数据。 主要是  


 首先 原来的SQL 。。

  with sql1 as (select vpfi.code code from t_p_info vpfi where vpfi.pm = '张三')
 select * from
  (select opp.username as user_name, 
  (select oi.insit_name from wbj_org_insit oi where oi.insit_id = oip.insit_id) as dname, 
  ea.pcode as pcode, 
(select min(decode(tt.type,'出差',nvl(tt.city, tt.type), tt.type)) from T_ATTENDANCE tt where tt.eid = opp.username and tt.date = '2015-08-01' ) as data_1, 
 --..... 依次
(select   min(decode(tt.type,'出差',nvl(tt.city, tt.type), tt.type)) from T_ATTENDANCE tt where tt.eid = opp.username and tt.date = '2015-08-31' ) as data_31 
 from t_per opp 
 left join t_post oip on opp.pid = oip.pid 
 left join t_attendance ea on ea.eid = opp.username and ea.month = '2015-08' 
 left join sql1 t1 on t1.code = ea.P_CODE 
 where oip.t_isldr > 0 and t1.code is not null and ( ea.RECORDER = '张三' or ea.emp_id = '张三') 
group by opp.username, oip.insit_id ,ea.prj_code )

 where 1=1 order by 2, 1 ;


原来的SQL 有 32 个标量子查询......另外表名写法也不统一,  看到这个SQL 大骇....    还是看了一下执行计划....    t_attendance  扫描多次,  逻辑读, 物理读 很多次。 T_ATTENDANCE   表如果数据量 很大, 哥可以保证,  这个SQL 跑死。。    这个表 扫描 30多次, 用的了吗?? 另外    (select oi.insit_name from wbj_org_insit oi where oi.insit_id = oip.insit_id) as dname,   谁能保证 值是唯一的???  当然业务上面原则是唯一的?? 但谁能 保证  数据库中值不会出现脏数据???( 库中没有唯一检查约束) ,另外之前 select min(decode(tt.type,'出差',nvl(tt.city, tt.type), tt.type)) from T_ATTENDANCE tt where tt.eid = opp.username and tt.date = '2015-08-01' ) 出现过脏数据,出错了, 所有 加了 min  , 执行计划就不 贴出来了。。。。反正是 乱的 不太想看........(  还有一个原因是 哥已经知道性能问题在哪, 另外改写什么的都知道大致怎么改写了, 所以哥不想看执行计划了。。。 兄弟们有时候还是要看执行计划的........ )  

还有 left join sql1 t1 on t1.code = ea.P_CODE     where 后面  and t1.code is not null  , 什么逻辑|||||    有用left join 又用 t1.code is not null ,,, 不如换成 inner join  去掉后面的 is not null 条件!!!!!  


还有一个问题是既然 是 group by 了。。。  就不要  order by , 因为 10 g  11g 中group by oracle 内部是排序 好的,  单官方的文档没有保证是的, 另外我们这边的业务上面 没有排序也满足... 那就不要 排序了.....  


于是改写后的  语句 


  with sql1 as (select   code from t_p_info vpfi where vpfi.pm = '张三')
    select  username, ea.pcode as pcode, oi.insit_name, 
    min( decode( date,'2015-08-01',decode(type,'出差',nvl(city, type), type)  ) ) day1,
   --- 一次31 
     min( decode( date,'2015-08-31',decode(type,'出差',nvl(city, type), type)  ) ) day31
    from t_per opp 
    inner join t_post oip on opp.pid = oip.pid 
    left join _insit oi on oi.insit_id = oip.insit_id
    left join t_attendance ea on ea.emp_id = opp.username  
    inner join sql1 t1 on t1.code = ea.PCODE 
    where oip.t_isldr > 0  and ( ea.RECORDER = '张三' or ea.emp_id = '张三') 
    and ea.month = '2015-08' 
    group by opp.username, oi.insit_name ,ea.prj_code;

改写后的执行计划也不贴出来了。。。。 避免了 上述的全部问题 ,  t_attendance 只扫描了1次,   在分组中取数据是   min, 等聚合函数取数据,

decode 行列 转换。  执行逻辑读 是原来的 1/10 这样。。。。 物理读 减少的也差不多》。


 decode( date,'2015-08-31',decode(type,'出差',nvl(city, type), type)  )   改换成  coalesce( nullif('出差', type),city, type) 好一点








0 0