一个非常不友好的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) 好一点
另外 主要涉及 到行列转化, 和在分组数据中 提取数据。 主要是
首先 原来的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) 好一点
相关文章推荐
- 时间序列数据库的秘密(1)—— 介绍
- sqlalchemy(二)高级用法
- mongodb删除数据库
- sql查询工程结算分包款转出
- MySQL索引
- MySQL查询执行路径
- MySQL主从
- mongodb创建数据库
- oracle 解锁scott账户
- 找到 mysql 数据库中的不良索引
- 简化实现动态行列转置的SQL
- vb.net它SqlHelper制备及应用
- MongoDB之分片
- 修改Oracle数据库的字符集为UTF-8
- 一次SQLSERVER触发器编写感悟
- Oracle 学习之SQL(一) Retrieving Data Using the SQL SELECT statement
- 辛星浅析plpgsql语法(中)
- yum方式安装mysql报错找不到mysql.sock
- MySQL CMake参数说明手册 【转载】
- MYSQL5.X重置root密码 (windows系统)