虽然Oracle提供的DETERMINISTIC声明,本意是确保函数的确定性,但是如何合理利用,是可以用来提高性能的。


这一篇描述参数顺序对性能的影响。






上一篇文章提到了,如果希望通过DETERMINISTIC来获取性能收益,那么采用批量方式是必须的,而且数组的值相对而言越大对于性能的帮助会越大。


但是这里存在一个问题,如果需要处理的数据量本身很大,虽然重复的输入参数不少,但是总的参数不同的值更多,那么即使将ARRAY的值设置到1000,能带来的性能收益也很有限,因为即使1000次调用,也不能保证参数完全一样出现多次,而如果无限制的增大ARRAY,虽然从DETERMINISTIC函数的角度讲可以提高性能,但是对于内存的占用等其他方面,会带来性能的下降,显然一味的扩大批量并不是问题的解决之道。


其实解决这个问题很简单,就是在调用DETERMINISTIC函数之前,对函数的参数进行排序,确保相同的参数顺序调用,这样DETERMINISTIC函数执行的次数最少。


继续使用上一篇文章的例子,看看排序后DETERMINISTIC函数的调用次数:


SQL> UPDATE T_DETER SET C = 1;


已更新15行。


已用时间: 00: 00: 00.17


SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;


      ID F_DETER(C) F_SYSDATE


---------- ---------- -------------------


       1         1 2011-05-26 14:26:12


       2         1 2011-05-26 14:26:13


       3         1 2011-05-26 14:26:13


       4         1 2011-05-26 14:26:13


       5         1 2011-05-26 14:26:13


       6         1 2011-05-26 14:26:13


       7         1 2011-05-26 14:26:13


       8         1 2011-05-26 14:26:13


       9         1 2011-05-26 14:26:13


      10         1 2011-05-26 14:26:13


      11         1 2011-05-26 14:26:13


      12         1 2011-05-26 14:26:13


      13         1 2011-05-26 14:26:13


      14         1 2011-05-26 14:26:13


      15         1 2011-05-26 14:26:13


已选择15行。


1


1


已用时间: 00: 00: 02.29


SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM (SELECT * FROM T_DETER ORDER BY C);


      ID F_DETER(C) F_SYSDATE


---------- ---------- -------------------


       1         1 2011-05-26 14:26:35


      15         1 2011-05-26 14:26:35


       3         1 2011-05-26 14:26:35


       4         1 2011-05-26 14:26:35


       5         1 2011-05-26 14:26:35


       6         1 2011-05-26 14:26:35


       7         1 2011-05-26 14:26:35


       8         1 2011-05-26 14:26:35


       9         1 2011-05-26 14:26:35


      10         1 2011-05-26 14:26:35


      11         1 2011-05-26 14:26:35


      12         1 2011-05-26 14:26:35


      13         1 2011-05-26 14:26:35


      14         1 2011-05-26 14:26:35


       2         1 2011-05-26 14:26:35


已选择15行。


1


已用时间: 00: 00: 01.10


可以看到,由于执行了排序,DETERMINISTIC函数不用再次检查输入参数是否相等,因此在SQL运行期间只调用了一次:


SQL> SHOW ARRAY


arraysize 15


SQL> SET ARRAY 1


SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM (SELECT * FROM T_DETER ORDER BY C);


      ID F_DETER(C) F_SYSDATE


---------- ---------- -------------------


       1         1 2011-05-26 14:28:31


      15         1 2011-05-26 14:28:31


       3         1 2011-05-26 14:28:31


       4         1 2011-05-26 14:28:31


       5         1 2011-05-26 14:28:31


       6         1 2011-05-26 14:28:31


       7         1 2011-05-26 14:28:31


       8         1 2011-05-26 14:28:31


       9         1 2011-05-26 14:28:31


      10         1 2011-05-26 14:28:31


      11         1 2011-05-26 14:28:31


      12         1 2011-05-26 14:28:31


      13         1 2011-05-26 14:28:31


      14         1 2011-05-26 14:28:31


       2         1 2011-05-26 14:28:31


已选择15行。


1


已用时间: 00: 00: 01.17


即使将ARRAY设置为1,也对DETERMINISTIC的调用次数没有影响了。


下面看看多个参数值的情况:


SQL> UPDATE T_DETER SET C = MOD(ROWNUM, 2);


已更新15行。


已用时间: 00: 00: 00.03


SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM (SELECT * FROM T_DETER ORDER BY C);


      ID F_DETER(C) F_SYSDATE


---------- ---------- -------------------


       2         0 2011-05-26 14:29:59


      14         0 2011-05-26 14:29:59


      12         0 2011-05-26 14:29:59


      10         0 2011-05-26 14:29:59


       8         0 2011-05-26 14:29:59


       6         0 2011-05-26 14:29:59


       4         0 2011-05-26 14:29:59


      11         1 2011-05-26 14:29:59


       7         1 2011-05-26 14:29:59


      13         1 2011-05-26 14:29:59


       5         1 2011-05-26 14:29:59


       9         1 2011-05-26 14:29:59


       3         1 2011-05-26 14:29:59


      15         1 2011-05-26 14:29:59


       1         1 2011-05-26 14:29:58


已选择15行。


1


0


已用时间: 00: 00: 02.08


即使ARRAY仍然为1,对于DETERMINISTIC函数也没有影响了,排序后DETERMINISTIC函数对于不同的参数值仅调用一次。


同时,这里也有一个有趣的现象,就是Oracle先执行的C=1的函数调用,然后才是C=0的调用。从SELECT查询返回的时间还有SERVEROUT的输出都证实了这一点,但是这与我们的理解不符,因为查询要求SQL先排序,然后运行函数,Oracle在这里并没有完全按照SQL的语法来运行,而是先调用了DETERMINISTIC函数,输出的时候再对结果排序。但由于指定了排序,事实上即使DETERMINISTIC函数即使在排序前调用,仍然使用了排序的特性,因此只运行了一次。


如果要使用DETERMINISTIC来提高函数的性能,最关键的一点是确保调用函数时,输入参数是排序的。


oracle视频教程请关注:http://u.youku.com/user_video/id_UMzAzMjkxMjE2.html