這幾天在寫一個存儲過程,反復優化了幾次,從最開始的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, '0', 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;
今天既學到了知識,又有工資拿,真是太開心啦,哈哈哈。。。
各位看官,如果有更好的建議,不吝賜教,歡迎指導。