Tuning a LIKE-clause to use Index

The LIKE-clause can ignore indexes, causing queries to run forever while doing full table scans. This document describes how to tune such SQL statements by making use of Oracle Text or reverse key indexes.

Tuning the ‘LIKE’ Clause:

Generally, search arguments in the WHERE clause such as "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'" prevents Oracle from using an index to perform the search (however, not always).

If you use LIKE in your WHERE clause, try to specify one or more leading characters if at all possible. For example, use LIKE 'm%' and not LIKE '%m'. If you specify a leading character, Oracle has a better chance of being able to use an index to perform the query - this will increase performance and reduce the load on the database server.

To avoid such full table scans, consider the following scenarios:

Case 1: Tuning the LIKE-clause by using Oracle Text indexes

Requirements:

A. Install and configure Oracle's TEXT (done as part of the installation process).

B. Check whether Oracle TEXT is installed by looking for the 'CTXSYS' schema.

The problem

I. Create a test table:

create table t as select * from tab;
CREATE INDEX normal_index ON t
(TABLE_NAME) NOPARALLEL;
 
SQL> select TABLE_NAME from t where TABLE_NAME LIKE '%SEG%';
TABLE_NAME
------------------------------
D_MKTSEG
SEG$

II. Run a query to demonstrate that it will do a full table scan:

SQL> set autotrace traceonly explain
SQL> select TABLE_NAME from t where TABLE_NAME LIKE '%SEG%';
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   TABLE ACCESS (FULL) OF 'T'

III. Drop the index:

SQL> drop index normal_index;
 
Index dropped.

Solution:

I. Create an Oracle Text index on the columns that you would like to search:

SQL> create index xyz_oracle_txt_idx on t(TABLE_NAME) INDEXTYPE IS CTXSYS.CONTEXT;
Index created.

II. Change the LIKE-clause to an CONTAINS-clause - WHERE CONTAINS(TABLE_NAME, '%SEG%') > 0;

SQL> select TABLE_NAME from t WHERE CONTAINS(TABLE_NAME, '%SEG%')>0;
 
TABLE_NAME
------------------------------
D_MKTSEG
SEG$

III. Look at the SQL plan:

SQL> set autotrace traceonly explain
SQL> select TABLE_NAME from t WHERE CONTAINS(TABLE_NAME, '%SEG%')>0;
 
Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=24)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'T' (Cost=2 Card=1 Bytes=
          24)
 
   2    1     DOMAIN INDEX OF 'XYZ_ORACLE_TXT_IDX' (Cost=0)

Much better!

Case 2: Tuning the LIKE-clause by using reverse key indexes

Another trick for indexing queries with a leading wildcard character (like '%SON') is to create a REVERSE index - and then programmatically reverse the LIKE-clause to read "LIKE 'NOS%'", effectively indexing on the other side of the text, clumsy, yet effective.

Steps:

1. Create reverse key index on columns that will be searched. For example, create a reverse key index on Cust_Name of the customer table:

CREATE INDEX Cust_Name_reverese_idx
ON customer(Cust_Name)
REVERSE;

2. Programmatically reverse the SQL LIKE-clause to read '%saliV%':

Existing Query:

SELECT * 
FROM customer
WHERE Cust_Name LIKE '%Vilas%'

New Query:

SELECT * 
FROM customer
WHERE Cust_Name LIKE '%saliV%';