前阵子遇到一个案例:一个同事说之前一个运行很正常的包,忽然间比之前慢了不少,执行时间很是长,晚上的做业调用这个包跑了几个小时也没有跑出数据。因而我在跟踪、优化过程当中定位到包中一个存储过程的一段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 挑战问题也述说了这样一种观念。开发