Scenario
Database
Table
- Users
- ID Number, PK
- CODE Number, Indexed, Unique, Not null
- FIRST_NAME VARCHAR(100), Not null
- LAST_NAME VARCHAR(100), Not null
- EMAIL VARCHAR(50), Not null
Search
- Except for ID and CODE, all other columns has mixed case.
- I need to search a user by an string. The search should be case insensitive.
- The string is a partial value. (ex: 'foo' to search for 'foobar')
- The pattern can be present on any part of a value on the colum. (ex: 'ob' for foobar)
- The pattern can be present on any of the table fields.
SQL
SELECT *
FROM users u
WHERE
u.code = COALESCE( to_number( REGEXP_REPLACE(:pattern, '[^0-9]+', '') ), -1)
OR upper(u.email) LIKE upper(:pattern)
OR upper(u.last_name) LIKE upper(:pattern)
OR upper(u.first_name) LIKE upper(:pattern)
Query Plan
- Results show a full table scan with an OR Filter Predicate
Question
- What is the best strategy to optimize this query? Can we reduce the cost somehow?
- Is beneficial to use an index on the VARCHAR2 column(s)? Even with the search using the LIKE operator?
- Can we use a different SQL(s) to take advantage of the indexes and join the results at the end?
Text Search Engines
- I'm aware that we could use an text search engine to handle that. But let's discuss what can be done using the database.
Related Links
Best Answer
No, forget
LIKE
operator for this, conditions asfirst_name LIKE '%name%'
will never be effective. A regular index can not be used for range scans for conditions asfirst_name LIKE '%name%'
. Iffirst_name
is indexed, index full scan is the best the database can use. And you still need to handle case-sensitity.(index range scan can be used for conditions as
first_name like 'name%'
, where the beginning of the value is fixed)This is a task for Oracle Text.
No, forget virtual columns, you can not create text indexes on virtual columns, this is what you get if you try it:
I have answered a similar question: Oracle “matches” multiple columns
But here it is again, with an example:
Then the query:
Notice how I did not specify all the columns, still the query searched in all of them (enumerated in the
multi_column_datastore
). I specifiedfirst_name
in the index and the query, yet the query returned records that matched on theemail
,code
orlast_name
column.Notice how I did not specify anything related to case-sensitivity in the query. It was specified with the
mixed_case=no
property (Oracle Text word searches are not case-insensitive by default: Case-Sensitive Searching).And the query uses the index: