Categorized | Oracle Tips

Function Based Index

Posted on 29 February 2008 by Praveen

Function Based Index

Why do you need a Function Based Index?

Considering the example of traditional “EMP” table, if you need to query the table based on emp_name then the query could be

SELECT * FROM EMP WHERE EMP_NAME =’JOE’;
Instead of making the full table scan on the above query you can create a non-unique index on the field “EMP_NAME”. By creating the index the above query makes use of index while execution.

if your requirement is to change the above query to

SELECT * FROM EMP WHERE UPPER(EMP_NAME)=’JOE’;

Since an function has been applied on EMP_NAME column, oracle will not utilize the index in executing the above query which will result in hampering the performance of your query. So Oracle has introduced Function Based Index to encounter the above problem.

1. You can create index based on function or Expression.

2. The session that you are trying to make use of FUNCTION INDEX should have the parameter QUERY_REWRITE_ENABLE is set to TRUE and QUERY_REWRITE_INEGRITY is set to TRUSTED. Oracle uses the Concept of Query rewriting i.e internal transformation of query, which is equivalent to source query but has different code to used function index.

3. Computed value of the function gets stored along with the rowid. So the function should be deterministic function. Deterministic means should always return the same value for the same parameter.

4.The function can also be a stored function and user should have execute privilege for that function.

5. Oracle maintains a time based dependencies between a function based index and the stored function that is used in the index creation.When function index is created, Oracle puts timestamp of creation. Naturally, this timestamp is more than timestamp of the function.
Later on if the Stored function is changed which is a having a timestamp greater than index, then oracle marks the index as invalid.
You can query the validity status of an index by the following query

Select STATUS from user_indexes where index_name=’index_name’;

The user can validate the Index structure using

ANALYZE INDEX INDEX_DUMMY VALIDATE STRUCTURE;

6. The decision whether to used index or not can be based on number of distinct values in that column. For function based inded this decision may not be take from distinct values of columns becuase different set of column values may give same value of expression.

7. To get the real benefit of Function based indexes , use histograms on the function based index columns.

Let us consider an example of a function based index.

I have a table called Symb_history which stores the historical data of stocks . A non unique index is available on the column ‘SYMBOL’.

The below query which doesn’t use a function

SQL> SELECT * FROM SYMB_HISTORY WHERE SYMBOL='EDUCOMP'; 
Execution Plan
 ---------------------------------------------------------- Plan hash value: 2881556207 

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

 | Id  | Operation                   | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | 

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

 |   0 | SELECT STATEMENT            |                        |  1208 | 48320 |    38   (0)| 00:00:01 | 

 |   1 |  TABLE ACCESS BY INDEX ROWID| SYMB_HISTORY           |  1208 | 48320 |    38   (0)| 00:00:01 | 

 |*  2 |   INDEX RANGE SCAN          | SYMB_HISTORY_IDX       |  1208 |       |     7   (0)| 00:00:01 | 

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

 Predicate Information (identified by operation id): 

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

    2 - access("SYMBOL"='EDUCOMP') 

Statistics 

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

           0  recursive calls 

           0  db block gets 

          75  consistent gets 

           0  physical reads 

           0  redo size 

       23733  bytes sent via SQL*Net to client 

         743  bytes received via SQL*Net from client 

          35  SQL*Net roundtrips to/from client 

           0  sorts (memory) 

           0  sorts (disk) 

         501  rows processed

If I change the above query to use UPPER function, then the index is not utilized, and also observe the CPU-COST.

SQL> select * from symb_history where upper(symbol)='EDUCOMP'; 
 Execution Plan
 ---------------------------------------------------------- Plan hash value: 3343617986 

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

 | Id  | Operation         | Name         | Rows  | Bytes | Cost (%CPU)| Time     | 

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

 |   0 | SELECT STATEMENT  |              | 37779 |  1475K|  6386   (5)| 00:01:17 | 

 |*  1 |  TABLE ACCESS FULL| SYMB_HISTORY | 37779 |  1475K|  6386   (5)| 00:01:17 | 

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

Predicate Information (identified by operation id): 

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

1 - filter(UPPER("SYMBOL")='EDUCOMP') 

Statistics 

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

           1  recursive calls 

           0  db block gets 

       22272  consistent gets 

       22221  physical reads 

           0  redo size 

       23733  bytes sent via SQL*Net to client 

         743  bytes received via SQL*Net from client 

          35  SQL*Net roundtrips to/from client 

           0  sorts (memory) 

           0  sorts (disk) 

         501  rows processed

So Let us now create a function based index on the UPPER function and the see the difference

Creating the Function based index


SQL> CREATE INDEX SYMB_FUN_IDX ON SYMB_HISTORY(UPPER(SYMBOL));

Analyzing the table after creating the function based index


SQL> ANALYZE TABLE SYMB_HISTORY COMPUTE STATISTICS;

Making rewriting possible for the cost based Optimizer


SQL> ALTER SESSION SET QUERY_REWRITE_INTEGRITY = TRUSTED;
SQL> ALTER SESSION SET QUERY_REWRITE_ENABLED = TRUE;

And the result … is ‘SYMB_FUND_IDX’ FUNCTION BASED INDEX is being used and CPU-cost has gone down

SQL> SELECT * FROM SYMB_HISTORY WHERE UPPER(SYMBOL)='EDUCOMP';Execution Plan
 ----------------------------------------------------------
 Plan hash value: 3467047201 

--------------------------------------------------------------------------------------------
 | Id  | Operation                   | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
 --------------------------------------------------------------------------------------------
 |   0 | SELECT STATEMENT            |              |  1208 | 48320 |    14   (0)| 00:00:01 |
 |   1 |  TABLE ACCESS BY INDEX ROWID| SYMB_HISTORY |  1208 | 48320 |    14   (0)| 00:00:01 |
 |*  2 |   INDEX RANGE SCAN          | SYMB_FUN_IDX |  1208 |       |     6   (0)| 00:00:01 |
 -------------------------------------------------------------------------------------------- 

Predicate Information (identified by operation id):
 --------------------------------------------------- 

2 - access(UPPER("SYMBOL")='EDUCOMP') 

Statistics
 ----------------------------------------------------------
           1  recursive calls
           0  db block gets
          75  consistent gets
           3  physical reads
           0  redo size
       23733  bytes sent via SQL*Net to client
         743  bytes received via SQL*Net from client
          35  SQL*Net roundtrips to/from client
           0  sorts (memory)
           0  sorts (disk)
         501  rows processed

Leave a Reply