这几天在写一个存储过程,反复优化了几次,从最开始的7分钟左右,优化到最后的几秒,并且这个过程中我的导师帮我指点了很多问题,这些指点都是非常宝贵的,独乐乐不如众乐乐,一起来分享这次的优化过程吧。

这个存过程的需求是这样的,抓取某个时间段内的订单明细,然后计算并汇总到某表即可。

于是乎,我写出第一版的存储过程,代码如下:

  /******************************************/
/* 合并当前版本时间段内MO的维修换料需求 */
/* p_begin 起始时间 */
/* p_user 创建人 */
/* p_version 版本编码 */
/* p_version 需求版本头表id */
/* Created by wufei in 2013-10-29 */
/******************************************/
procedure clc_Mat_Addition_Require(p_begin date,
p_user varchar2,
x_result out varchar2,
x_msg out varchar2) is
v_count int; --处理行数
v_num number; --维修换料数
v_version_code mms_mo_ori_version.version_code%type; --版本号
v_version_id mms_mo_ori_version.version_id%type; --需求单头号
v_raise exception;
begin
v_version_code:=to_char(p_begin,'yyyyMMdd');
v_version_id := fun_mms_get_guid();
--查询历史版本表,已执行过不允许执行
select count(*) into v_count from mms_mo_ori_version mmov
where mmov.version_code = to_char(p_begin,'yyyyMMdd'); if v_count>0 then
raise v_raise;
end if; v_count:=0;
--生成新版本头数据
insert into mms_mo_ori_version
(version_id,
version_code,
start_time,
end_time,
creation_date,
created_by,
mat_type)
values
(v_version_id,
v_version_code,
p_begin,
p_begin+1,
sysdate,
p_user,
1);--类别:维修换料 for line in (
select cwr.inventory_item_id,cwr.item_code,cwr.description,sum(cwr.quantity_open) as quantity_open
from ifce.cux_wip_requirement_v cwr,
ifce.cux_wip_entity_all_v cwal
where cwr.WIP_ENTITY_ID = cwal.WIP_ENTITY_ID
and cwal.START_DATE >= p_begin
and cwal.START_DATE < p_begin+1
and cwr.quantity_open > 0
group by cwr.INVENTORY_ITEM_ID,cwr.item_code,cwr.description
)
loop --获取维修换料数
select ifce.wip_logistics.fun_get_Iqcquit_sum(line.ITEM_CODE,trunc(p_begin))
into v_num from dual;
if (v_num >0) then --当维修换料需求比例数大于0时插入
insert into mms_mo_mat_require
(mat_requireid,
mat_id,
mat_code,
mat_desc,
require_time,
require_qty,
status,
creation_date,
created_by,
version,
mat_type,
super_market_inv_code)
select fun_mms_get_guid(),
line.inventory_item_id,
line.item_code,
line.description,
p_begin,
line.quantity_open*v_num,
'',
sysdate,
p_user,
v_version_code,
1,
'42B'
from dual;
v_count:=v_count+1;
end if; end loop; commit;
x_result:='Y';
x_msg:=to_char(v_count);
exception
when v_raise then
x_result:='N';
x_msg:='当前日期已执行过维修换料运算。';
when others then
rollback;
x_result:='N';
x_msg:='程序异常';
end clc_Mat_Addition_Require;

代码是没有问题,运行结果也没有问题,但就是慢,经过导师指点,“cwr.WIP_ENTITY_ID = cwal.WIP_ENTITY_ID”这里是有问题,这两个表之间用这种方式连接,索引会不起作用,并且这个表的时间没有加索引,综合起来就比较慢了,大概需要7秒才能运行完成。

找到了问题之后,就开始了改写。

改写的逻辑是这样的:

1,首先不使用这种连接方式,并且从另外一个本地表中用时间做过滤,这个时间是有索引的。

2,从车间排程表中,查询出需要运行的单据,并遍历。

3,遍历单据的明细。

4,插入维修换料数据。

5,对已插入的维修换料数汇总。

改写之后代码如下:

  /******************************************/
/* Mo维修换料需求运算 */
/* p_begin 起始时间 */
/* p_user 创建人 */
/* p_version 版本编码 */
/* p_version 需求版本头表id */
/* Created by wufei in 2013-10-29 */
/******************************************/
procedure clc_Mat_Addition_Require3(p_begin date,
p_user varchar2,
x_result out varchar2,
x_msg out varchar2) is
v_count int; --处理行数
v_num number; --维修换料数
v_version_code mms_mo_ori_version.version_code%type; --版本号
v_version_id mms_mo_ori_version.version_id%type; --需求单头号
v_raise exception;
v_wareHouse mms_dictionary_item.input_code1%type; --补料仓代码
begin
v_version_code:=to_char(p_begin,'yyyyMMdd');
v_version_id := fun_mms_get_guid();
--查询字典表里的补料仓代码
select mdi.input_code1 into v_wareHouse from mms_dictionary_item mdi
where code='AdditionWarehouse';
--查询历史版本表,已执行过不允许执行
select count(*) into v_count from mms_mo_ori_version mmov
where mmov.version_code = to_char(p_begin,'yyyyMMdd'); if v_count>0 then
raise v_raise;
end if; v_count:=0;
--生成新版本头数据
insert into mms_mo_ori_version
(version_id,
version_code,
start_time,
end_time,
creation_date,
created_by,
mat_type)
values
(v_version_id,
v_version_code,
p_begin,
p_begin+1,
sysdate,
p_user,
1);--类别:维修换料 for line in (
select wdps.wip_entity_id
from mms_wdps wdps
where wdps.plan_date >= p_begin
and wdps.plan_date <= p_begin+1
)
loop
for detailLine in (
select cwr.inventory_item_id,cwr.item_code,cwr.description,sum(cwr.quantity_open) as quantity_open
from ifce.cux_wip_requirement_v cwr
where cwr.WIP_ENTITY_ID = line.wip_entity_id
and cwr.quantity_open > 0
group by cwr.INVENTORY_ITEM_ID,cwr.item_code,cwr.description
)
loop
--获取维修换料数
select ifce.wip_logistics.fun_get_Iqcquit_sum(detailLine.ITEM_CODE,trunc(p_begin))
into v_num from dual;
if (v_num >0) then --当维修换料需求比例数大于0时插入
insert into mms_mo_mat_require
(mat_requireid,
mat_id,
mat_code,
mat_desc,
require_time,
require_qty,
status,
creation_date,
created_by,
version,
mat_type,
super_market_inv_code)
select fun_mms_get_guid(),
detailLine.inventory_item_id,
detailLine.item_code,
detailLine.description,
p_begin,
detailLine.quantity_open*v_num,
0,
sysdate,
p_user,
v_version_code,
5,--将Mat_type改为5,稍后过滤,汇总
v_wareHouse
from dual;
v_count:=v_count+1;
end if;
end loop;
end loop;
commit;
--将当天插入的维修换料汇总
insert into mms_mo_mat_require
(mat_requireid,
mat_id,
mat_code,
mat_desc,
require_time,
require_qty,
status,
creation_date,
created_by,
version,
mat_type,
super_market_inv_code)
(select fun_mms_get_guid(),
mat_id,
mat_code,
mat_desc,
p_begin,
sum(require_qty) as qty,
0,
sysdate,
p_user,
v_version_code,
1,--类别:维修换料
v_wareHouse
from mms_mo_mat_require mr
where mr.mat_type=5 and mr.version=v_version_code
group by mat_id,
mat_code,
mat_desc);
delete from mms_mo_mat_require where mat_type=5 and version=v_version_code;
commit; x_result:='Y';
x_msg:=to_char(v_count);
exception
when v_raise then
rollback;
x_result:='N';
x_msg:='当前日期已执行过维修换料运算。';
when others then x_result:='N';
x_msg:='程序异常';
end clc_Mat_Addition_Require3;

此时,运行效率已大大提升,测试了一下,只要0.42秒,但被导师看了之后,又提了几个问题。

1,程序中不应该使用两次commit;因为同一个会话中的数据是可以在检索到的,所以并不一定要提交到数据库才可以查看。

2,使用汇总插入并删除原来的数据也是不对的,因为针对数据库来说,删除是要写日志记录,耗费大量资源的。

所以针对此问题,又做了改动,把汇总并删除改为更新或插入。

代码如下:

  procedure clc_Mat_Addition_Require4(p_begin   date,
p_user varchar2,
x_result out varchar2,
x_msg out varchar2) is
v_count int; --处理行数
v_num number; --维修换料数
v_version_code mms_mo_ori_version.version_code%type; --版本号
v_version_id mms_mo_ori_version.version_id%type; --需求单头号
v_raise exception;
v_wareHouse mms_dictionary_item.input_code1%type; --补料仓代码
v_item_count int; --物料明细行数;
begin
v_version_code:=to_char(p_begin,'yyyyMMdd');
v_version_id := fun_mms_get_guid();
--查询字典表里的补料仓代码
select mdi.input_code1 into v_wareHouse from mms_dictionary_item mdi
where code='AdditionWarehouse';
--查询历史版本表,已执行过不允许执行
select count(*) into v_count from mms_mo_ori_version mmov
where mmov.version_code = to_char(p_begin,'yyyyMMdd'); if v_count>0 then
raise v_raise;
end if; v_count:=0;
--生成新版本头数据
insert into mms_mo_ori_version
(version_id,
version_code,
start_time,
end_time,
creation_date,
created_by,
mat_type)
values
(v_version_id,
v_version_code,
p_begin,
p_begin+1,
sysdate,
p_user,
1);--类别:维修换料 for line in (
select wdps.wip_entity_id
from mms_wdps wdps
where wdps.plan_date >= p_begin
and wdps.plan_date <= p_begin+1
)
loop
for detailLine in (
select cwr.inventory_item_id,cwr.item_code,cwr.description,sum(cwr.quantity_open) as quantity_open
from ifce.cux_wip_requirement_v cwr
where cwr.WIP_ENTITY_ID = line.wip_entity_id
and cwr.quantity_open > 0
group by cwr.INVENTORY_ITEM_ID,cwr.item_code,cwr.description
)
loop
--获取维修换料数
select ifce.wip_logistics.fun_get_Iqcquit_sum(detailLine.ITEM_CODE,trunc(p_begin))
into v_num from dual;
if (v_num >0) then --当维修换料需求比例数大于0时插入
select count(*) into v_item_count
from mms_mo_mat_require
where mat_code=detailLine.item_code and version=v_version_code; if(v_item_count>0) then
update mms_mo_mat_require set require_qty= round(require_qty+detailLine.quantity_open*v_num)
where mat_code=detailLine.item_code and version=v_version_code;
else
insert into mms_mo_mat_require
(mat_requireid,
mat_id,
mat_code,
mat_desc,
require_time,
require_qty,
status,
creation_date,
created_by,
version,
mat_type,
super_market_inv_code,
attribute4)
select fun_mms_get_guid(),
detailLine.inventory_item_id,
detailLine.item_code,
detailLine.description,
p_begin,
round(detailLine.quantity_open*v_num),
0,
sysdate,
p_user,
v_version_code,
1,
v_wareHouse,
detailLine.quantity_open||' '||v_num||' '||line.wip_entity_id
from dual;
end if;
v_count:=v_count+1;
end if;
end loop;
end loop;
commit;
x_result:='Y';
x_msg:=to_char(v_count);
exception
when v_raise then
rollback;
x_result:='N';
x_msg:='当前日期已执行过维修换料运算。';
when others then
x_result:='N';
x_msg:='程序异常';
end clc_Mat_Addition_Require4;

今天既学到了知识,又有工资拿,真是太开心啦,哈哈哈。。。

各位看官,如果有更好的建议,不吝赐教,欢迎指导。

记一次Sql优化过程的更多相关文章

  1. SQL优化过程中常见Oracle HINT

    在SQL语句优化过程中,我们经常会用到hint,现总结一下在SQL优化过程中常见Oracle HINT的用法: 1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量, ...

  2. 记一次SQL优化。

    程序是数据库的用户,为打造良好的用户体验,我们一直在努力. 此次介绍一个基于SQL的数据库优化.SQL的优劣对数据库的性能影响非常关键. 查询只涉及如下表结构中的三个字段.如下 开发原始SQL SEL ...

  3. 记一次SQL优化

    常见的SQL优化 一.查询优化 1.避免全表扫描 模糊查询前后加%也属于全表扫描 在where子句中对字段进行表达式操作会导致引擎放弃使用索引而进行全表扫描,如: select id from t w ...

  4. 记一次sql优化——left join不走索引问题

    sql一执行就卡住,然后就...杀进程了 看了一下表的大小 第一反应就是加索引,然后explain看了一下走什么索引了,结果很尴尬,三个表,只走了一个索引...一群人在那纠结为毛走不了索引. 无意间发 ...

  5. 4W条人才表循环处理业务sql优化过程

    场景: 使用windows服务定时更新合同数据:执行存储过程(pas_RefreshContractStatus),但存储过程里面有一个需要更新4W条人才表循环处理业务 问题: 循环更新4W条人才表状 ...

  6. 转载:MYSQL数据库三表联查的SQL优化过程

    地址:https://database.51cto.com/art/202002/609803.htm 作者用了三张有设计缺陷的表做例子,使得优化效果空前,优化手段仅为拨乱反正和加索引,此行可为一哂.

  7. ORACLE常用SQL优化hint语句

    在SQL语句优化过程中,我们经常会用到hint,现总结一下在SQL优化过程中常见Oracle HINT的用法: 1. /*+ALL_ROWS*/ 表明对语句块选择基于开销的优化方法,并获得最佳吞吐量, ...

  8. 其实SQL优化调优,就跟吃饭喝水一样简单,教你抓住SQL的本质!

    前言 SOL 优化并不简单,做好 SOL 优化需要掌握数据库体系结构.表和索引设计.高效 SOL法.高级 SOL 语法.多种优化工具等知识,甚至还得分析业务特点,以及了解优化器的缺点.只有建立 SOL ...

  9. SQL优化原理

    SQL优化过程: 1,捕获高负荷的SQL语句-->2得到SQL语句的执行计划和统计信息--->3分析SQL语句的执行计划和统计信息--->4采取措施,对SQL语句进行调整.1找出高负 ...

随机推荐

  1. Fedora 命令

    1. 更新包 yum clear all yum -y update 2.yum包查找 yum whatprovides xxxx.os.l 3 df 查看磁盘空间 xclip 复制到粘贴板 xcli ...

  2. OFBIZ bug_ControlServlet.java:233:ERROR

    错误日志: [java] 2014-09-26 10:12:17,031 (http-bio-0.0.0.0-8443-exec-5) [ ControlServlet.java:233:ERROR] ...

  3. 三、mysql运算符

    取模有2种方法: 或 mod(,) 比较一个字段的值是不是null有两种方法:is null 或 <=> null 不能直接使用 where id = null;是不对的

  4. Java内存区域与内存溢出异常(二)

    了解Java虚拟机的运行时数据区之后,大致知道了虚拟机内存的概况,内存中都放了些什么,接下来将了解内存中数据的其他细节,如何创建.如何布局.如何访问.这里虚拟机以HotSpot为例,内存区域以Java ...

  5. 制作输入框(Input)

    怎样判断是否应当使用输入框 输入框,就是用户可以自由输入文本的地方.当需要判断是否需要使用输入框时,可以遵循一条原则:凡是需要用户自主输入文本的地方,几乎都必须使用输入框. 输入框的常见用法:输入登录 ...

  6. mybatis随意sql语句

    mybatis的mapper.xml随意sql语句, 不管表之间存不存在关系, 都可以使用, 但注意resultMap中一定要指定查询数据返回的列 或 对象(其实就是多列封装到一个对象中) <? ...

  7. (转)[C++语法] 关键字typedef用法

    转自http://www.cnblogs.com/SweetDream/archive/2006/05/10/395921.html C/C++语言中的typedef相信大家已经不陌生,本文对C/C+ ...

  8. 1074: [SCOI2007]折纸origami - BZOJ

    Description 桌上有一张边界平行于坐标轴的正方形纸片,左下角的坐标为(0,0),右上角的坐标为(100,100).接下来执行n条折纸命令.每条命令用两个不同点P1(x1,y1)和P2(x2, ...

  9. 解决position:relative情况下,z-index无效的方法

    在实际开发中,div+css经常会碰到层级的问题 其中有个很头痛的就是z-index控制层级时,老是发现z-index不起作用 老杨依据自己的经验,总结出以下步骤: 1.判断被覆盖的层(想要置顶的层) ...

  10. Atmel Studio 6.0 重新安装

    问题描述:        Atmel Studio 6.0 重新安装     在卸载Atmel Studio6.0之后,重新安装Atmel Studio6.0软件,提示cannot find one ...