Making Oracle Case insensitive

Making Oracle Case insensitive

Well, this article is the fruit of thoughts on which how you can make Oracle's output case insensitive. Of course, the simplest way is to use the UPPER() function to change the case of either data stored or data being checked. For Example:

SELECT * 

FROM emp

WHERE UPPER(ename) = 'RAVI';

Function Based Index 

But by following this method, there is one problem. If the column ename is having an index, it will no longer be used. But starting from Oracle 8i, there is a concept called function-based index. Before getting to use function-based indexes, the following criteria must be met with:

  • You must have the system privilege query rewrite to create function based indexes on tables in your own schema.
  • You must have the system privilege global query rewrite to create function based indexes on tables in other schema's.
  • For the optimizer to use function based indexes, the following session or system variables must be set:
    QUERY_REWRITE_ENABLED=TRUE
    QUERY_REWRITE_INTEGRITY=TRUSTED
  • You must be using the Cost Based Optimizer (which means analyzing your tables/indexes)

And then it’s just a case of creating the index in the conventional way:

create index UPPER_ENAME_IX on ENAME ( UPPER(ENAME) ) ;

Note that this index will NOT be used for case-SENSITIVE queries. You could always have two indexes, one on ENAME and one on UPPER(ENAME), but it would probably be more efficient to just have the function-based one and code.

REGEXP_LIKE

With the release of Oracle version 10g, the regular expressions came to help for solving the problem. The following query can be executed to get case insensitive output:

SELECT * 

FROM emp

WHERE REGEXP_LIKE(ename,'ravi','i');

The output is:

ENAME               
--------------------
rAvi                
RAVI                
Ravi    

NLSSORT Function

There is one more way by which we can achieve this output. We can create a function-based index using nlssort() function. Like:

CREATE INDEX empp_idx ON
empp(NLSSORT(ename,'NLS_SORT=BINARY_CI'));

The above approach will not invalidate the index, and will hold a good candidate for using case-insensitive queries.

There is one more approach where it is no required  to change the queries. We just need to update two oracle system parameters, NLS_COMP and NLS_SORT. The following example will suffice:

NLS_COMP, NLS_SORT

alter session set NLS_COMP=ANSI;

alter session set NLS_SORT=BINARY_CI;

select ename from empp where ename = 'ravi';

ENAME               
--------------------
rAvi                
RAVI                
Ravi    
  

This approach will be far more efficient because of two reasons:

1. Indexes will be used if it exists on the column.

2. No development (rework) is required to make queries insensitive.

Click here to read How to Change Oracle back to Case Sensitive once again

14 comments :

  1. How can i create a FBI on a statement like this

    SQL>select count(rowid ) from AUDS where AUDSEARCH like :b

    SQLTUNING ADVISOR SUGGESTS to create a function based index on the expression. (There is an index existing on AUDSEARCH)

    How does create index statement look like for above expression?
    Is this correct
    create index fbi_auds on auds('audsearch like :b');


    Here is the explain plan

    1- Restructure SQL finding (see plan 1 in explain plans section)
    ----------------------------------------------------------------
    The predicate "AUDS"."AUDSEARCH" LIKE :B1 used at line ID 2 of the
    execution plan contains an expression on indexed column "AUDSEARCH". This
    expression prevents the optimizer from selecting indices on table
    "SHPS"."AUDS".

    Recommendation
    --------------
    - Rewrite the predicate into an equivalent form to take advantage of
    indices. Alternatively, create a function-based index on the expression.

    Rationale
    ---------
    The optimizer is unable to use an index if the predicate is an inequality
    condition or if there is an expression or an implicit data type conversion
    on the indexed column.

    -------------------------------------------------------------------------------
    EXPLAIN PLANS SECTION
    -------------------------------------------------------------------------------

    1- Original
    -----------
    Plan hash value: 2023312948

    -----------------------------------------------------------------------------
    | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
    -----------------------------------------------------------------------------
    | 0 | SELECT STATEMENT | | 1 | 63 | 3684 (3)| 00:00:45 |
    | 1 | SORT AGGREGATE | | 1 | 63 | | |
    |* 2 | TABLE ACCESS FULL| AUDS | 10571 | 650K| 3684 (3)| 00:00:45 |
    -----------------------------------------------------------------------------

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

    2 - filter(INTERNAL_FUNCTION("AUDSEARCH") LIKE :B)

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

    Thanks
    S~

    ReplyDelete
  2. this is good information!

    ReplyDelete
  3. Hi,

    Its really a good represent of the article as how you hv describe the different way to make oracle case insensetive.

    Can you pls help me more.--
    -- How can i see the Current Session value of "NLS" and "NLS_SORT" by command line. I mean if any time i want to revert back make oracel case SENSETIVE (not insensetive). Then how it is possible. Pls if possible then copy a mail at "biswassagar@gmail.com". Any way thanks for the article.



    Thanks and Regard
    Sagar Biswas.
    New Delhi.
    biswassagar@gmail.com

    ReplyDelete
  4. To make oracle case sensitive again issue the following command:

    alter session set NLS_SORT=GENERIC_M_CI;

    ReplyDelete
  5. thanks for the example it helped me lot(i am new to oracle)

    ReplyDelete
  6. Hi,

    Does this solutions (NLS_SORT and NLS_COMP) work also with LIKE statement?

    I mean will
    alter session set NLS_COMP=ANSI;

    alter session set NLS_SORT=BINARY_CI;

    select ename from empp where ename LIKE '%ravi%';

    still be case insensitive?

    ReplyDelete
  7. Thanks Anantha for the article..nicely written.

    1: The approach you have suggested
    NLS_COMP, NLS_SORT is it the "best way" since this might make every query case insensitive..not sure if that is performance heavy.

    2: NLSSORT Function- will this help overcome the limitation of above ? If so, does ths need changing anything in the session ?

    Thanks
    Samant

    ReplyDelete
  8. Filips,

    Try
    ALTER SESSION SET nls_comp=LINGUISTIC;
    alter session set nls_sort=binary_ci;

    The above 2 work for LIKE as well.

    ReplyDelete
  9. Great article.. Here is how to make the whole database case-insensitive in just two steps:
    http://geekzspot.blogspot.com/2010/01/case-insensitive-oracle-database.html

    (Hope it helps)

    ReplyDelete
  10. Hi,

    Have you came across a scenario when the FBI is not getting used when NLS_SORT=binary_ci and NLS_COMP=linguistic and "like" is used in the where condidtion ? However, if I change the where clause to use "=" instead, the FBI is getting used, but with "like" it's doing a FTS.

    ReplyDelete
  11. This is a known bug:

    In some cases Function Based Indexes are not used when using LIKE and NLS_COMP=LINGUISTIC Details:
    When the string of the LIKE operator does not contain % LIKE predicates will be internally converted to equivalent predicates as "=, >=, .." etc. will provoke that in the case LIKE is rewritten internally (!)

    FBI are not considered for usage.
    Fixed releases: 11.1.0.8 11gR2

    ReplyDelete
  12. beautiful, thanks ñ_ñ

    ReplyDelete
  13. Is there a way to make single column in database table as case sensitive. Dont want to make whole database case insensitive

    ReplyDelete
  14. I don't know if any single column in database table as case insensitive. But assume that Oracle will never need to provide such a feature. Is there any real need for this?

    ReplyDelete