tags:

views:

434

answers:

1

By default Oracle uses indexes created.

When I change to NLS_COMP=Linguistic and NLS_Sort=Binary_CI, I get full table scans.

I'd read somewhere that creating an index using (nlssort(name, 'NLS_SORT=BINARY_CI')); Would work.

As my attempt below shows, not so much. Even if I force it, the performance does not seem to be what I would expect. This is a trivial example I like to solve this for a table with many millions of rows, so full table scans would be bad.

So the question is how to I build indexes so they will be used.

Thanks

-- Setup X

create table x ( name varchar2(30)) ;
insert into x select table_name from all_tables;
create index x_ix on x (name);
create index x_ic on x (nlssort(name, 'NLS_SORT=BINARY_CI'));
/

-- Default Settings

ALTER SESSION SET NLS_COMP=BINARY;
ALTER SESSION SET NLS_SORT=BINARY; 
/
set autotrace on 
/
select * from X where NAME like 'x%';

--0 rows selected
--
---------------------------------------------------------------------------
--| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
--|   0 | SELECT STATEMENT |      |     1 |    17 |     1   (0)| 00:00:01 |
--|*  1 |  INDEX RANGE SCAN| X_IX |     1 |    17 |     1   (0)| 00:00:01 |
---------------------------------------------------------------------------
/
set autotrace off
/

-- Linguistic

ALTER SESSION SET NLS_COMP=LINGUISTIC; 
ALTER SESSION SET NLS_SORT=BINARY_CI; 
/
set autotrace on
/
select * from X where NAME like 'x%';
--13 rows selected
--
----------------------------------------------------------------------------
--| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
--|   0 | SELECT STATEMENT  |      |     1 |    17 |     3   (0)| 00:00:01 |
--|*  1 |  TABLE ACCESS FULL| X    |     1 |    17 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

select /*+ INDEX( X  X_IX           ) */ * from X where   NAME like 'x%';
--13 rows selected
--
---------------------------------------------------------------------------
--| Id  | Operation        | Name | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------
--|   0 | SELECT STATEMENT |      |     1 |    17 |     9   (0)| 00:00:01 |
--|*  1 |  INDEX FULL SCAN | X_IX |     1 |    17 |     9   (0)| 00:00:01 |
---------------------------------------------------------------------------

select /*+ INDEX( X  X_IC           ) */ * from X where   NAME like 'x%'; 
--13 rows selected
--
--------------------------------------------------------------------------------------
--| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------
--|   0 | SELECT STATEMENT            |      |     1 |    17 |   448   (1)| 00:00:06 |
--|*  1 |  TABLE ACCESS BY INDEX ROWID| X    |     1 |    17 |   448   (1)| 00:00:06 |
--|   2 |   INDEX FULL SCAN           | X_IC |  1629 |       |     8   (0)| 00:00:01 |
--------------------------------------------------------------------------------------
/

set autotrace off
/
+1  A: 

Hi Brian,

I have reproduced your finding on my test DB (10.2.0.3). Upon investigation, it appears the LIKE operator cannot use the linguistic index -- from the 10gR2 Documentation:

The SQL functions MAX( ) and MIN( ), and also the LIKE operator, cannot use linguistic indexes when NLS_COMP is set to LINGUISTIC.

It seems the main purpose of linguistic indexes is to improve the SORT operation.

If your goal is to search on this column in a case-insensitive way, I suggest you create an index on UPPER(name) and build your query with UPPER(name) LIKE UPPER('x%') instead.

If you want to use another (more complex) linguistic setting, you might want to look at the Oracle Text indexes.

Edit: There is another workaround: you can replace the LIKE 'ABC%' with:

SQL> select * from x where name >= 'ABC' and name < 'ABD';

Execution Plan
----------------------------------------------------------
Plan hash value: 708878862

--------------------------------------------------------------------------------
| Id  | Operation                   | Name | Rows  | Bytes | Cost (%CPU)| Time  
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |      |     1 |    24 |     4   (0)| 00:00:
|   1 |  TABLE ACCESS BY INDEX ROWID| X    |     1 |    24 |     4   (0)| 00:00:
|*  2 |   INDEX RANGE SCAN          | X_IC |     1 |       |     3   (0)| 00:00:
--------------------------------------------------------------------------------

As you can see if you can translate the LIKE expression to an expression with the comparison operators (> and <) the linguistic index might be used.

Vincent Malgrat