記一次Sql優化過程


這幾天在寫一個存儲過程,反復優化了幾次,從最開始的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;     
View Code

代碼是沒有問題,運行結果也沒有問題,但就是慢,經過導師指點,“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;     
View Code

此時,運行效率已大大提升,測試了一下,只要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;     
View Code

 

今天既學到了知識,又有工資拿,真是太開心啦,哈哈哈。。。

 

各位看官,如果有更好的建議,不吝賜教,歡迎指導。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM