ORACLE應用調優:請避免SQL做大量循環邏輯處理


    前陣子遇到一個案例:一個同事說以前一個運行很正常的包,突然間比以前慢了很多,執行時間非常長,晚上的作業調用這個包跑了幾個小時也沒有跑出數據。於是我在跟蹤、優化過程中定位到包中一個存儲過程的一段SQL,我將原SQL簡化了一下(對應的表名、函數全都隨機取名替換掉),大體如下所示,在一個游標中,循環更新表TMP_JO_ORDERS, 其中需要通過函數獲取一些值,這些值用來更新目標表的字段值

 

FOR CUR_JO IN (SELECT JOB_ORDER_NO FROM TMP_JO_ORDERS WHERE SEW_START >=SYSDATE ) LOOP

        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'SEWING','BUTTON')   INTO MY_M_BUTTON FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'SEWING','LABEL')    INTO MY_M_LABEL  FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'SEWING','TAPE')     INTO MY_M_TAPE   FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'SEWING','ZIPPER')   INTO MY_M_ZIPPER FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'SEWING','OTHERS')   INTO MY_M_OTHERS FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'THREAD','ALL')      INTO MY_M_THREAD FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'INTERLINING','ALL') INTO MY_M_INTERLINING FROM DUAL;
        SELECT MAIN_ITC.GET_MUST_INFO(CUR_JO.JOB_ORDER_NO,'PACKING','ALL')     INTO MY_M_PACKING FROM DUAL;
       
        UPDATE TMP_JO_ORDERS A
        SET M_BUTTON=MY_M_BUTTON
             ,M_LABEL=MY_M_LABEL
             ,M_TAPE=MY_M_TAPE
             ,M_ZIPPER=MY_M_ZIPPER
             ,M_OTHERS=MY_M_OTHERS
             ,M_THREAD=MY_M_THREAD
             ,M_INTERLINING=MY_M_INTERLINING
             ,M_PACKING=MY_M_PACKING
        WHERE JOB_ORDER_NO=CUR_JO.JOB_ORDER_NO;
END LOOP;

 

其實以前運行正常,突然出現性能問題,是因為SELECT JOB_ORDER_NO FROM TMP_JO_ORDERS WHERE SEW_START >=SYSDATE的數據量由於業務量突然增加了很多,所以游標的循環次數從以前幾十次突然飈增到8千多次。

假設游標里面的SQL執行時間需要2秒,以前只循環了30次,那么運算該SQL需要2*30=60秒,如果循環次數突然飈增到8000次,2*8000=16000秒,這就是幾個小時的時間。你可以想象一下,這個性能會突然下降到一種無法忍受的程度!

那么怎么優化呢? 當然是減少循環次數。仔細觀察了這段SQL,弄明白寫這個SQL的老兄的業務邏輯后,上面的循環處理完全可以用下面一個SQL語句替換,完全沒有必要一條記錄一條記錄更新。當時修改后測試,發現修改后的SQL,不到1分鍾就運行出來了。

UPDATE TMP_JO_ORDERS A
        SET M_BUTTON     =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'SEWING','BUTTON')
             ,M_LABEL      =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'SEWING','LABEL')
             ,M_TAPE       =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'SEWING','TAPE')
             ,M_ZIPPER     =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'SEWING','ZIPPER')
             ,M_OTHERS     =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'SEWING','OTHERS')
             ,M_THREAD     =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'THREAD','ALL')
             ,M_INTERLINING=MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'INTERLINING','ALL')
             ,M_PACKING    =MAIN_INTERFACE.GET_MUST_INFO(JOB_ORDER_NO,'PACKING','ALL')
    WHERE SEW_START >=SYSDATE;

 

     其實這只是一個特殊的案例,我只是將其當做一個引子,引入我想闡述的觀點:我們知道SQL是結構化查詢語言,擅長於結構化查詢,而不擅長於邏輯處理(WHIE、IF..ELSE),但是有時候,很多人喜歡用SQL來處理業務邏輯,當然也不是說不能在存儲過程、函數里面做一些業務邏輯處理,只是發現不少人過度放大SQL的邏輯處理功能,將復雜的邏輯運算全部搬到包、存儲過程里面處理,例如上面的循環運算,這樣做的一個糟糕結果就是性能問題,就好像一個擅長於短跑的人,你硬要他去參加長跑。那么比賽結果肯定不會好到哪里去。

 

    在開發中,我們要對業務邏輯做一些優化處理,避免復雜的邏輯運算,尤其避免循環次數非常大的業務邏輯處理,一方面我們要簡化業務邏輯,有些業務邏輯運算轉到程序中去處理,另外一方面我們可以用SQL很巧妙的實現很多邏輯復雜的需求,避免我們去做大量復雜的邏輯處理,而不要在復雜的業務下寫出更加復雜的SQL語句.例如上面的例子,我以前在一篇文章MS SQL 挑戰問題也述說了這樣一種觀念。


免責聲明!

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



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