dingjun123@ORADB> SELECT * FROM t WHERE get_date(a)=DATE’2013-5-1′;
A
———-
2013
1 row selected.
Execution Plan
———————————————————-
Plan hash value: 1594971208
————————————————————————————-
| Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ?| Rows ?| Bytes | Cost (%CPU)| Time ? ? |
————————————————————————————-
| ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? | ? ? 1 | ? ?16 | ? ? 1 ? (0)| 00:00:01 |
| ? 1 | ?TABLE ACCESS BY INDEX ROWID| T ? ? | ? ? 1 | ? ?16 | ? ? 1 ? (0)| 00:00:01 |
|* ?2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX_T | ? ? 1 | ? ? ? | ? ? 1 ? (0)| 00:00:01 |
————————————————————————————-
Predicate Information (identified by operation id):
—————————————————
? ?2 – access(“DINGJUN123″.”GET_DATE”(“A”)=TO_DATE(‘ 2013-05-01 00:00:00′,
? ? ? ? ? ? ? ‘syyyy-mm-dd hh24:mi:ss’))
|
dingjun123@ORADB> SELECT * FROM t WHERE get_date(a)=DATE’2013-5-1′;
一个
——-
2013年
已选择 1 行。
执行计划
————————————————————-
计划哈希值:1594971208
——————————————————————————————-
|身份证号?|手术 ? ? ? ? ? ? ? ? ? |姓名?|行?|字节 |成本(%CPU)|时间 ? ? |
——————————————————————————————-
| ? 0 |选择语句? ? ? ? ? ?| ? ? ? | ? ? 1 | ? ?16 | ? ? 1 ? (0)| 00:00:01 |
| ? 1 | ?按索引 ROWID 访问表| T? ? | ? ? 1 | ? ?16 | ? ? 1 ? (0)| 00:00:01 |
|* ?2 | ?索引范围扫描? ? ? ? ?| IDX_T | ? ? 1 | ? ? ? | ? ? 1 ? (0)| 00:00:01 |
——————————————————————————————-
谓词信息(通过操作id标识):
——————————————————
? ?2 – access(“DINGJUN123”.”GET_DATE”(“A”)=TO_DATE(‘ 2013-05-01 00:00:00′,
? ? ? ? ? ? ? ‘syyyy-mm-dd hh24:mi:ss’)) |
表>? ???上面的结果是令人迷惑的,因为表里存储的有2行2013,但是最终结果却只查询出一行。究其原因,就是自定义函数虽然使用了DETERMINISTIC关键字,但是ORACLE只管有没有这关键字,而不会管你的函数逻辑是否真的对每个相同的输入,有一样的输出,这里我们使用DETERMINISTIC关键字,欺骗了ORACLE。很显然,虽然在表里存储的2行都是2013,但是一个5月份插入的,一个6月份插入的,通过函数运算,一个索引中存储的是2013-5-1,一个是2013-6-1,所以使用2013-5-1里查询的时候,只返回1行。如果自定义中有类似于DBMS_RANDOM,SYS_GUID等不确定或随时间变化值不同的,那么也会产生此混乱结果。
? 另外很多书上说函数索引必须:
? ? ? ??ORACLE使用函数索引,会进行查询重写,要求下面两个参数开启:? ? ? ?
? ? ?? QUERY_REWRITE_ENABLED=TRUE
? ? ? ? QUERY_REWRITE_INTEGRITY=TRUSTED
? 经过测试,发现在本环境11g下无影响,后来看了yangtingkun大师的文章,原来早就没有影响了。http://space.itpub.net/4227/viewspace-68620
针对第2点的例子:
? ???函数索引的函数定义不能随便改变,改变就必须rebuild函数索引(or删除重建),因为函数索引中会存储对应函数运算的结果,然后在使用函数索引访问的时候,不用再调用函数,so,函数改变,oracle不会级联rebuild其函数索引,所以,改变函数逻辑不手动rebuild,必然是危险的。
???走全表扫描,函数会对每行都调用1次(当然DETERMINSTIC函数是可以有缓存效果的,以后再说明):
dingjun123@ORADB> DROP TABLE tt;
Table dropped.
dingjun123@ORADB> CREATE TABLE ?tt(NAME VARCHAR2(10));
Table created.
dingjun123@ORADB> INSERT INTO tt
? 2 ? ? ?SELECT LEVEL FROM dual CONNECT BY LEVEL < 1000;
999 rows created.
–DBMS_APPLICATION_INFO包监控函数的调用次数
dingjun123@ORADB> CREATE OR REPLACE FUNCTION func_tt(x IN VARCHAR2)
? 2 ?RETURN VARCHAR2 DETERMINISTIC AS
? 3 ?BEGIN
? 4 ? ? ?DBMS_APPLICATION_INFO.set_client_info(USERENV(‘client_info’) 1 );
? 5 ? ? ?RETURN ‘o’ || x;
? 6 ?END;
? 7 ?/
Function created.
dingjun123@ORADB> EXEC DBMS_APPLICATION_INFO.set_client_info(0);
PL/SQL procedure successfully completed.
dingjun123@ORADB> SELECT * FROM tt WHERE func_tt(NAME) <= ‘mmmmm6′;
no rows selected dingjun123@ORADB> select userenv(‘client_info’) from Dual;
USERENV('CLIENT_INFO')
—————————————————————-
999
已选择 1 行。 |
? ?无函数索引,全表扫描,访问对每行都调用函数,一条SQL访问函数999次。如果使用函数索引,那么必然在创建(DML)的时候,会自动调用函数,索引中存储对应的key与函数运算结果值,所以,再使用到函数索引的时候,不用再调用函数,而且索引访问还提高效率,达到多种提高效率的效果。
–重置计数器
dingjun123@ORADB> EXEC DBMS_APPLICATION_INFO.set_client_info(0);
PL/SQL procedure successfully completed.
dingjun123@ORADB> CREATE INDEX ?idx_tt ON tt(func_tt(NAME));
Index created.
–创建索引的时候就调用函数了
dingjun123@ORADB> select userenv(‘client_info’) from dual;
USERENV(‘CLIENT_INFO’)
—————————————————————-
999
1 row selected.
dingjun123@ORADB> set autotrace traceonly
–使用的时候不再调用函数,因为已经调用过函数,函数运算的结果已经存储到索引中了
dingjun123@ORADB> SELECT * FROM tt WHERE func_tt(NAME) = ‘o1′;
1 row selected.
Execution Plan
———————————————————-
Plan hash value: 6977672
————————————————————————————–
| Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? |
————————————————————————————–
| ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ? ?10 | 20090 | ? ? 2 ? (0)| 00:00:01 |
| ? 1 | ?TABLE ACCESS BY INDEX ROWID| TT ? ? | ? ?10 | 20090 | ? ? 2 ? (0)| 00:00:01 |
|* ?2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX_TT | ? ? 4 | ? ? ? | ? ? 1 ? (0)| 00:00:01 |
————————————————————————————–
Predicate Information (identified by operation id):
—————————————————
? ?2 – access(“DINGJUN123″.”FUNC_TT”(“NAME”)=’o1′)
Note
—–
? ?- dynamic sampling used for this statement (level=2)
Statistics
———————————————————-
? ? ? ? ?24 ?recursive calls
? ? ? ? ? 0 ?db block gets
? ? ? ? ?14 ?consistent gets
? ? ? ? ? 0 ?physical reads
? ? ? ? ? 0 ?redo size
? ? ? ? 417 ?bytes sent via SQL*Net to client
? ? ? ? 416 ?bytes received via SQL*Net from client
? ? ? ? ? 2 ?SQL*Net roundtrips to/from client
? ? ? ? ? 0 ?sorts (memory)
? ? ? ? ? 0 ?sorts (disk)
? ? ? ? ? 1 ?rows processed
dingjun123@ORADB> select userenv(‘client_info’) from Dual;
USERENV('CLIENT_INFO')
—————————————————————-
999
已选择 1 行。
|
? ? 使用自定义函数索引是危险的,如果修改函数定义,没有rebuild或删除重建函数索引,那么函数索引中存储的还是旧的函数运算结果,这样会导致错误:
dingjun123@ORADB> CREATE OR REPLACE FUNCTION func_tt(x IN VARCHAR2)
? 2 ?RETURN VARCHAR2 DETERMINISTIC AS
? 3 ?BEGIN
? 4 ? ? ?DBMS_APPLICATION_INFO.set_client_info(USERENV(‘client_info’) 1 );
? 5 ? ???RETURN ‘a’ || x;
? 6 ?END;
? 7 ?/
Function created.
–查询不对,函数应该运算结果’o1′应该没有行,但是因为索引没有被rebuild
dingjun123@ORADB> SELECT * FROM tt WHERE func_tt(NAME) = ‘o1′;
NAME
———-
1
1 row selected.
–强制全表扫描,正确
dingjun123@ORADB> SELECT/* full(tt)*/ * FROM tt WHERE func_tt(NAME) = ‘o1′;
no rows selected
–rebuild索引后也正确
dingjun123@ORADB> alter index idx_tt rebuild;
Index altered.
dingjun123@ORADB> set autotrace traceonly
dingjun123@ORADB> SELECT * FROM tt WHERE func_tt(NAME) = ‘o1′;
no rows selected
Execution Plan
———————————————————-
Plan hash value: 6977672
————————————————————————————–
| Id ?| Operation ? ? ? ? ? ? ? ? ? | Name ? | Rows ?| Bytes | Cost (%CPU)| Time ? ? |
————————————————————————————–
| ? 0 | SELECT STATEMENT ? ? ? ? ? ?| ? ? ? ?| ? ?10 | 20090 | ? ? 2 ? (0)| 00:00:01 |
| ? 1 | ?TABLE ACCESS BY INDEX ROWID| TT ? ? | ? ?10 | 20090 | ? ? 2 ? (0)| 00:00:01 |
|* ?2 | ? INDEX RANGE SCAN ? ? ? ? ?| IDX_TT | ? ? 4 | ? ? ? | ? ? 1 ? (0)| 00:00:01 |
————————————————————————————–
Predicate Information (identified by operation id):
—————————————————
? ?2 – access(“DINGJUN123″.”FUNC_TT”(“NAME”)=’o1′)
|
? ? 在不得不使用函数索引来提高效率的时候,别忘记了,随时准备维护函数索引,而且别弄出奇奇怪怪的函数索引,导致乱七八糟的问题,那样就不好了!
原文地址:自定义函数索引使用及其注意点, 感谢原作者分享。