前陣子遇到一個案例:一個同事說之前一個運行很正常的包,忽然間比之前慢了不少,執行時間很是長,晚上的做業調用這個包跑了幾個小時也沒有跑出數據。因而我在跟蹤、優化過程當中定位到包中一個存儲過程的一段SQL,我將原SQL簡化了一下(對應的表名、函數全都隨機取名替換掉),大致以下所示,在一個遊標中,循環更新表TMP_JO_ORDERS, 其中須要經過函數獲取一些值,這些值用來更新目標表的字段值html
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分鐘就運行出來了。code
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;htm
其實這只是一個特殊的案例,我只是將其當作一個引子,引入我想闡述的觀點:咱們知道SQL是結構化查詢語言,擅長於結構化查詢,而不擅長於邏輯處理(WHIE、IF..ELSE),可是有時候,不少人喜歡用SQL來處理業務邏輯,固然也不是說不能在存儲過程、函數裏面作一些業務邏輯處理,只是發現很多人過分放大SQL的邏輯處理功能,將複雜的邏輯運算所有搬到包、存儲過程裏面處理,例如上面的循環運算,這樣作的一個糟糕結果就是性能問題,就好像一個擅長於短跑的人,你硬要他去參加長跑。那麼比賽結果確定不會好到哪裏去。blog
在開發中,咱們要對業務邏輯作一些優化處理,避免複雜的邏輯運算,尤爲避免循環次數很是大的業務邏輯處理,一方面咱們要簡化業務邏輯,有些業務邏輯運算轉到程序中去處理,另一方面咱們能夠用SQL很巧妙的實現不少邏輯複雜的需求,避免咱們去作大量複雜的邏輯處理,而不要在複雜的業務下寫出更加複雜的SQL語句.例如上面的例子,我之前在一篇文章MS SQL 挑戰問題也述說了這樣一種觀念。開發