您的位置:首页 > 数据库

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

2015-08-21 09:54 344 查看
这个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) 好一点
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: