標籤:blog http ar color sp for 資料 on art
前陣子遇到一個案例:一個同事說以前一個運行很正常的包,突然間比以前慢了很多,執行時間非常長,晚上的作業調用這個包跑了幾個小時也沒有跑出資料。於是我在跟蹤、最佳化過程中定位到包中一個預存程序的一段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是結構化查詢語言 (SQL),擅長於結構化查詢,而不擅長於邏輯處理(WHIE、IF..ELSE),但是有時候,很多人喜歡用SQL來處理商務邏輯,當然也不是說不能在預存程序、函數裡面做一些商務邏輯處理,只是發現不少人過度放大SQL的邏輯處理功能,將複雜的邏輯運算全部搬到包、預存程序裡面處理,例如上面的迴圈運算,這樣做的一個糟糕結果就是效能問題,就好像一個擅長於短跑的人,你硬要他去參加長跑。那麼比賽結果肯定不會好到哪裡去。
在開發中,我們要對商務邏輯做一些最佳化處理,避免複雜的邏輯運算,尤其避免迴圈次數非常大的商務邏輯處理,一方面我們要簡化商務邏輯,有些商務邏輯運算轉到程式中去處理,另外一方面我們可以用SQL很巧妙的實現很多邏輯複雜的需求,避免我們去做大量複雜的邏輯處理,而不要在複雜的業務下寫出更加複雜的SQL語句.例如上面的例子,我以前在一篇文章MS SQL 挑戰問題也述說了這樣一種觀念。
ORACLE應用調優:請避免SQL做大量迴圈邏輯處理