虽然Oracle提供的DETERMINISTIC声明,本意是确保函数的确定性,但是如何合理利用,是可以用来提高性能的。
这一篇描述ARRAY对性能的影响。
关于DETERMINISTIC函数,以前已经写过一些文章了,不过对于DETERMINISTIC声明用来提高性能只是简单提了一句,并没有展开来说。
由于函数声明了DETERMINISTIC特性,Oracle对于相同的输入,可以只运行一次,而这对于代码比较复杂,调用时间较长的函数而言,确实可以提高性能。
但是在上面的几篇文章中也提到了,DETERMINISTIC是基于调用的,因此使用DETERMINISTIC不但与输入参数是否重复有关,也与SQL调用次数有关。
一个前面文章提到过的简单的例子就是sqlplus的数组方式FETCH数据,不同的array的值,就会影响DETERMINISTIC函数的运行次数,哪怕
不过这里要澄清以前一个错误的观点,由于设置ARRAY为1后,访问DETERMINISTIC函数发现每两条记录调用一次,当时认为ARRAY方式的最小值是2,但是现在发现,问题和ARRAY无关,导致问题的原因和DETERMINISTIC的实现算法有关。
由于DETERMINISTIC并不像RESULT_CACHE那样,在单独的内存区域中保存每次调用的结果,因此Oracle需要判断DETERMINISTIC函数两次输入是否一样,这对于输入参数相同的情况还简单一些,但是对于包含大量变化的变量,就使得函数的调用次数很难预料。
看一个简单的例子来说明这个问题:
SQL> CREATE OR REPLACE FUNCTION F_DETER (V_IN NUMBER)
2 RETURN NUMBER DETERMINISTIC AS
3 BEGIN
4 DBMS_LOCK.SLEEP(1);
5 DBMS_OUTPUT.PUT_LINE(V_IN);
6 RETURN V_IN;
7 END;
8 /
函数已创建。
SQL> CREATE TABLE T_DETER (ID NUMBER, C NUMBER);
表已创建。
SQL> INSERT INTO T_DETER
2 SELECT ROWNUM, 1
3 FROM TAB;
已创建15行。
SQL> SET SERVEROUT ON
SQL> SET TIMING ON
SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
会话已更改。
已用时间: 00: 00: 00.09
SQL> SHOW ARRAY
arraysize 15
SQL> SELECT ID, F_DETER(C), SYSDATE FROM T_DETER;
ID F_DETER(C) SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:10:54
2 1 2011-05-26 08:10:54
3 1 2011-05-26 08:10:54
4 1 2011-05-26 08:10:54
5 1 2011-05-26 08:10:54
6 1 2011-05-26 08:10:54
7 1 2011-05-26 08:10:54
8 1 2011-05-26 08:10:54
9 1 2011-05-26 08:10:54
10 1 2011-05-26 08:10:54
11 1 2011-05-26 08:10:54
12 1 2011-05-26 08:10:54
13 1 2011-05-26 08:10:54
14 1 2011-05-26 08:10:54
15 1 2011-05-26 08:10:54
已选择15行。
1
1
已用时间: 00: 00: 02.80
SQL> CREATE OR REPLACE FUNCTION F_SYSDATE RETURN DATE AS
2 BEGIN
3 RETURN SYSDATE;
4 END;
5 /
函数已创建。
已用时间: 00: 00: 00.04
SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;
ID F_DETER(C) F_SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:11:13
2 1 2011-05-26 08:11:14
3 1 2011-05-26 08:11:14
4 1 2011-05-26 08:11:14
5 1 2011-05-26 08:11:14
6 1 2011-05-26 08:11:14
7 1 2011-05-26 08:11:14
8 1 2011-05-26 08:11:14
9 1 2011-05-26 08:11:14
10 1 2011-05-26 08:11:14
11 1 2011-05-26 08:11:14
12 1 2011-05-26 08:11:14
13 1 2011-05-26 08:11:14
14 1 2011-05-26 08:11:14
15 1 2011-05-26 08:11:14
已选择15行。
1
1
已用时间: 00: 00: 02.18
由于对于SYSDATE函数,Oracle做了优化,因为查询中看到的是同一个时间,将SYSDATE封装到一个函数中,可以看到,即使对于ARRAY设置为15的情况,在同一次的调用中,输入参数完全相同,DETERMINISTIC函数也会调用两次。这个就与DETERMINISTIC的具体实现有关系了。
对于DETERMINISTIC和ARRAY的关系以前也描述过,这里不详细展开了,下面看看当参数发生变化的情况:
SQL> UPDATE T_DETER SET C = MOD(ROWNUM, 2);
已更新15行。
已用时间: 00: 00: 00.10
SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;
ID F_DETER(C) F_SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:46:02
2 0 2011-05-26 08:46:03
3 1 2011-05-26 08:46:04
4 0 2011-05-26 08:46:04
5 1 2011-05-26 08:46:04
6 0 2011-05-26 08:46:04
7 1 2011-05-26 08:46:04
8 0 2011-05-26 08:46:04
9 1 2011-05-26 08:46:04
10 0 2011-05-26 08:46:04
11 1 2011-05-26 08:46:04
12 0 2011-05-26 08:46:04
13 1 2011-05-26 08:46:04
14 0 2011-05-26 08:46:04
15 1 2011-05-26 08:46:04
已选择15行。
1
0
1
已用时间: 00: 00: 03.22
SQL> SET ARRAY 5
SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;
ID F_DETER(C) F_SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:46:50
2 0 2011-05-26 08:46:51
3 1 2011-05-26 08:46:52
4 0 2011-05-26 08:46:52
5 1 2011-05-26 08:46:52
6 0 2011-05-26 08:46:52
7 1 2011-05-26 08:46:53
8 0 2011-05-26 08:46:54
9 1 2011-05-26 08:46:54
10 0 2011-05-26 08:46:54
11 1 2011-05-26 08:46:54
12 0 2011-05-26 08:46:55
13 1 2011-05-26 08:46:56
14 0 2011-05-26 08:46:56
15 1 2011-05-26 08:46:56
已选择15行。
1
0
1
1
0
0
1
已用时间: 00: 00: 07.50
SQL> SET ARRAY 2
SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;
ID F_DETER(C) F_SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:48:25
2 0 2011-05-26 08:48:26
3 1 2011-05-26 08:48:27
4 0 2011-05-26 08:48:28
5 1 2011-05-26 08:48:29
6 0 2011-05-26 08:48:30
7 1 2011-05-26 08:48:31
8 0 2011-05-26 08:48:32
9 1 2011-05-26 08:48:33
10 0 2011-05-26 08:48:34
11 1 2011-05-26 08:48:35
12 0 2011-05-26 08:48:36
13 1 2011-05-26 08:48:37
14 0 2011-05-26 08:48:38
15 1 2011-05-26 08:48:39
已选择15行。
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
已用时间: 00: 00: 15.38
可以看到,仅仅是两个不同参数的交替出现,在ARRAY为5的情况下,Oracle的DETERMINISTIC的调用方式已经很复杂了,不过有一定是确定的,当ARRARY设置为2时,DETERMINISTIC将完全失效,这种情况下,得不到任何性能上的好处。
而如果不同参数的个数设置为3个,则情况会更加复杂:
SQL> UPDATE T_DETER SET C = MOD(ROWNUM, 3);
已更新15行。
已用时间: 00: 00: 00.03
SQL> SET ARRAY 15
SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;
ID F_DETER(C) F_SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:55:59
2 2 2011-05-26 08:56:00
3 0 2011-05-26 08:56:01
4 1 2011-05-26 08:56:02
5 2 2011-05-26 08:56:02
6 0 2011-05-26 08:56:02
7 1 2011-05-26 08:56:02
8 2 2011-05-26 08:56:02
9 0 2011-05-26 08:56:02
10 1 2011-05-26 08:56:02
11 2 2011-05-26 08:56:02
12 0 2011-05-26 08:56:02
13 1 2011-05-26 08:56:02
14 2 2011-05-26 08:56:02
15 0 2011-05-26 08:56:02
已选择15行。
1
2
0
1
已用时间: 00: 00: 04.25
SQL> SET ARRAY 5
SQL> SELECT ID, F_DETER(C), F_SYSDATE FROM T_DETER;
ID F_DETER(C) F_SYSDATE
---------- ---------- -------------------
1 1 2011-05-26 08:56:19
2 2 2011-05-26 08:56:20
3 0 2011-05-26 08:56:21
4 1 2011-05-26 08:56:22
5 2 2011-05-26 08:56:22
6 0 2011-05-26 08:56:22
7 1 2011-05-26 08:56:23
8 2 2011-05-26 08:56:24
9 0 2011-05-26 08:56:25
10 1 2011-05-26 08:56:25
11 2 2011-05-26 08:56:25
12 0 2011-05-26 08:56:26
13 1 2011-05-26 08:56:27
14 2 2011-05-26 08:56:28
15 0 2011-05-26 08:56:28
已选择15行。
1
2
0
1
1
2
0
0
1
2
已用时间: 00: 00: 10.19
虽然DETERMINISTIC的调用实现很复杂,但是这里至少可以得出一个结论,如果希望通过DETERMINISTIC来获取性能收益,那么采用批量方式是必须的,而且数组的值相对而言越大对于性能的帮助会越大,因此每次数组获取数据,被认为是一次调用,而DETERMINISTIC函数主要是在一次调用中生效。当然根据上面测试的结果,跨调用的生效也是可能存在的,但是多次调用必然会导致函数更多次数的调用,如果仅从DETERMINISTIC函数的调用效果考虑,ARRAY值越大,对于性能的提高就越大。
oracle视频教程请关注:http://u.youku.com/user_video/id_UMzAzMjkxMjE2.html