`
jayghost
  • 浏览: 436609 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

使用Oracle函数索引 提高查询效率

 
阅读更多

使用Oracle函数索引,无疑是提高查询效率的有效方法之一。下面就为您详细介绍Oracle函数索引的使用方法,希望对您能有所帮助。

谈到任何对列的操作都可能导致全表扫描,例如:

  1. select * from emp where substr(ename,1,2)=’SM’; 

但是这种查询在客服系统又经常使用,我们可以创建一个带有substr函数的基于Oracle函数索引,

  1. create index emp_ename_substr on eemp ( substr(ename,1,2) ); 

但是这种查询在客服系统又经常使用,我们可以创建一个带有substr函数的Oracle函数索引,

  1. create index emp_ename_substr on eemp ( substr(ename,1,2) ); 

这样在执行上面的查询语句时,这个基于函数的索引将排上用场,执行计划将是(INDEX RANGE SCAN)。
上面的例子中,我们创建了基于函数的索引,但是如果执行下面的查询:

  1. select * from emp where substr(ename,1,1)=’S’ 

得到的执行计划将还是(TABLE ACCESS FULL),因为只有当数据列能够等式匹配时,基于函数的索引才能生效,这样对于这种索引的计划和维护的要求都很高。请注意,向表中添加索引是非常危险的操作,因为这将导致许多查询执行计划的变更。然而,如果我们使用基于函数的索引就不会产生这样的问题,因为Oracle只有在查询使用了匹配的内置函数时才会使用这种类型的索引。

 

【Oracle index】自定义函数索引使用及其注意点 

转:http://blog.chinaunix.net/uid-7655508-id-3708041.html

当我们对列使用了函数运算之后,如果此列没有函数索引,那么普通索引是无效的。比如where substr(name,1,3)='abc';如果建立了create INDEX idx_t ON t(NAME);
  那么谓词是无法使用此索引做范围扫描的。在oracle中允许定义函数索引(FUNCTION BASED INDEX,简称FBI),函数索引可以是基于内置函数的,也可以是自定义函数的,
  本文主要讲述基于自定义函数的索引用法及其注意点。

        当需要对列进行复杂的运算,复杂的规则需要自定义函数的时候,如果需要走索引,那么必须建立自定义函数的索引。建立自定义函数索引有几点要注意:
       1.自定义函数必须加DETERMINISTIC关键字,让ORACLE知道此函数对于每个入参的返回结果都是确定的唯一的。
        道理很明显,如果一样的入参,结果不同,那么查询的结果必然有问题,必须要用这个关键字告诉ORACLE,此函数索引是可以信任的。但是有个问题得注意:因为自定义         函数是一系列逻辑规则,就算定义的函数对每个入参返回的值不唯一(比如用了SYSDATE,RANDOM等运算),但是使用了DETERMINISTIC关键字,让ORACLE相信唯             一,事实不唯一,那么使用函数索引查询的结果必然也是有问题的。所以使用函数索引要注意:必须从逻辑上确定对于一样的入参返回的结果是一样的,因为ORACLE不会         检查你的逻辑。
      
       2.一旦改变函数定义,必须REBUILD对应的函数索引
         很显然,函数索引中存储的是表中的列或表达式作为自定义函数的参数的运算结果,如果函数改变,ORACLE不会自动REBUILD函数索引对应的值,这样如果继续使用函数         索引,必然结果可能出错。

     下面分别对上面的内容举例说明:
针对第1点的例子:

--使用自定义函数索引,必须加DETERMINISTIC,并且实际对应一样的输入参数,返回的结果就是一样的,否则会导致错误
dingjun123@ORADB> CREATE OR REPLACE FUNCTION get_date(param_in VARCHAR2)
  2  RETURN DATE DETERMINISTIC
  3  AS
  4  BEGIN
  5   RETURN TO_DATE(param_in,'yyyy');
  6  END;
  7  /

Function created.

dingjun123@ORADB> DROP TABLE t;
Table dropped.

dingjun123@ORADB> CREATE TABLE t(a VARCHAR2(10));
Table created.

dingjun123@ORADB> CREATE INDEX idx_t ON t(get_date(a));
Index created.

--2013-年5月份插入
dingjun123@ORADB> INSERT INTO t VALUES('2013');
1 row created.

dingjun123@ORADB> commit;
Commit complete.

dingjun123@ORADB> SELECT * FROM t WHERE get_date(a)=DATE'2013-5-1';
A
----------
2013
1 row selected.


  OF COURSE,现在的结果是没有问题的,但是本身这个自定义函数中的TO_DATE(param,'yyyy')针对不同月份的插入结果返回的都是当月的第一天,如果我是6月插入:

--2013年6月份插入
dingjun123@ORADB> INSERT INTO t VALUES('2013');

1 row created.

dingjun123@ORADB> COMMIT;
Commit complete.


dingjun123@ORADB> alter session set nls_date_format='yyyy-mm-dd hh24:mi:ss';
Session altered.

dingjun123@ORADB> select a from t;
A
----------
2013
2013
2 rows selected.


 现在是查询:

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'))


     上面的结果是令人迷惑的,因为表里存储的有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 row selected.


   无函数索引,全表扫描,访问对每行都调用函数,一条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 row selected.


    使用自定义函数索引是危险的,如果修改函数定义,没有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')


    在不得不使用函数索引来提高效率的时候,别忘记了,随时准备维护函数索引,而且别弄出奇奇怪怪的函数索引,导致乱七八糟的问题,那样就不好了!

 

分享到:
评论

相关推荐

    合理创建和使用索引 提高Oracle查询效率.pdf

    "合理创建和使用索引提高Oracle查询效率" 索引是Oracle数据库管理系统中的一种重要数据结构。它可以大大提高查询效率,减少I/O操作。索引可以是B+树索引、簇索引、散列簇索引、反序索引、位图索引和函数索引等多种...

    Oracle提高查询效率的方法

    "Oracle 提高查询效率的方法" Oracle数据管理系统作为...提高 Oracle 查询效率需要从多方面考虑,包括优化 SQL 语句、创建合理的索引、选择合适的基础表、调整 WHERE 子句的顺序、避免使用 DISTINCT 和 ORDER BY 等。

    Oracle数据库索引优化方法探析.pdf

    使用函数索引可以提高查询效率,降低资源消耗。 Oracle 数据库索引优化的重要性在于,它可以提高数据库的查询效率和性能,减少资源消耗,提高用户体验。 Oracle 数据库管理员需要深刻理解 Oracle 机制,采用更加...

    提高Oracle查询效率

    - **减少子查询**:子查询可能导致多次表访问,合并子查询或使用连接操作有时能提高效率。 - **使用EXISTS替代IN**:当需要检查子查询结果是否包含主查询中的记录时,EXISTS通常比IN更快,因为它一旦找到匹配就...

    oracle 性能优化 加快查询效率

    - **函数索引的使用**:尽管不推荐使用,但在某些场景下,如频繁使用的函数查询,可以考虑使用函数索引来提高性能。 - **视图索引的限制**:虽然视图也可以添加索引,但由于维护成本较高,通常不推荐这样做。 #### ...

    提高查询效率的oracle索引优化策略探析.pdf

    2. **避免全表扫描**:使用函数索引(Function-based Index, FBI)可以显著提高特定查询的速度,且在更新未涉及索引列时不会带来额外开销。 3. **分析索引使用情况**:定期评估索引的使用频率,删除不必要的索引以...

    oracle中索引的使用

    在Oracle数据库系统中,索引是一种重要的数据结构,它能够显著提升数据查询的效率。索引的使用是数据库性能优化的关键环节,尤其是在大型企业级应用中,对索引的合理设计和管理对于系统的运行速度至关重要。这篇博文...

    oracle函数大全(CHM格式).rar

    "Oracle函数大全(CHM格式).rar"这个压缩包显然包含了关于Oracle数据库中各种函数的详细信息,对于学习和查询Oracle函数是非常宝贵的资源。 CHM(Compiled Help Manual)格式是一种由Microsoft开发的帮助文件格式...

    Oracle索引分析与查询优化.pdf

    Oracle 查询优化是指对 Oracle 查询语句的优化,以提高查询效率和降低查询时间。查询优化可以通过调整查询语句、使用索引、优化数据库设计等方式来实现。本文中,我们将对 Oracle 索引分析与查询优化进行详细的介绍...

    Oracle提高SQL查询效率(SQL优化).doc

    本文将详细介绍 Oracle 中提高 SQL 查询效率的技巧和策略,包括选择最有效率的表名顺序、WHERE 子句中的连接顺序、SELECT 子句中避免使用‘*’、减少访问数据库的次数、使用 DECODE 函数、整合简单的数据库访问、...

    Oracle索引分析与比较

    当查询涉及到对某列应用相同函数时,如`SELECT * FROM table WHERE function(column) = value`,函数索引能够显著提高查询速度。系统可以直接使用索引来匹配函数的结果,无需在查询时计算函数值。 在选择适合的索引...

    09 oracle的索引 PPT

    索引可以帮助快速查找和访问数据库中的数据,显著提高查询效率。本篇将详细讲解Oracle数据库中的索引及其重要性。 一、索引的概念与类型 1. 索引是一种特殊的数据结构,它为数据库表中的列提供快速访问路径。就像书...

    oracle的索引学习

    位图索引则适用于低基数(即不同值较少)的列,或者在进行多列组合查询时,可以将多个位图索引合并,以提高查询效率。反向键索引适用于排序顺序与键值顺序相反的情况,如主键自增的情况。 当涉及数据操作,如删除、...

    提高ORACLE数据库的查询统计速度

    - **避免使用SELECT ***:指定需要查询的字段可以减少数据传输量,提高效率。 - **合理使用JOIN操作**:正确地连接表可以避免不必要的数据加载和处理开销。 - **减少子查询使用**:尽可能将多个子查询合并成一个复杂...

    ORACLE 优化sql语句提高oracle执行效率 .doc

    ORACLE 优化 SQL 语句提高 Oracle 执行效率 Oracle 是一个功能强大的关系数据库管理系统,然而,如果不正确地使用 SQL 语句,可能会导致执行效率低下。为了提高 Oracle 的执行效率,需要遵循一些最佳实践。下面是...

    Oracle索引优化

    函数索引允许对列应用函数后再创建索引,使得查询可以使用函数结果进行查找。 了解索引的基本概念后,我们关注组合索引。组合索引是由多个列组成的索引,它可以提供更灵活的查询优化。在Oracle9i之前,只有当查询...

    ORACLE索引详解及SQL优化

    4. **选择合适的数据类型**:使用最小的数据类型可以降低存储需求,提高查询效率。 5. **避免在索引列上进行计算**:索引不会包含基于索引列的计算结果,因此这样的查询将无法使用索引。 此外,了解并运用Oracle的...

    ORACLE分析函数.ppt

    使用分析函数可以减少表或索引的访问次数,降低逻辑读取和COST,提高查询效率和数据处理速度。此外,分析函数还可以实现复杂的业务逻辑,简化查询语句,提高开发效率。 2. 分析函数的缺点 分析函数需要排序操作,...

Global site tag (gtag.js) - Google Analytics