264

The default behaviour of LIKE and the other comparison operators, = etc is case-sensitive.

Is it possible make them case-insensitive?

Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275
sergionni
  • 13,290
  • 42
  • 132
  • 189

7 Answers7

348

There are 3 main ways to perform a case-insensitive search in Oracle without using full-text indexes.

Ultimately what method you choose is dependent on your individual circumstances; the main thing to remember is that to improve performance you must index correctly for case-insensitive searching.

1. Case your column and your string identically.

You can force all your data to be the same case by using UPPER() or LOWER():

select * from my_table where upper(column_1) = upper('my_string');

or

select * from my_table where lower(column_1) = lower('my_string');

If column_1 is not indexed on upper(column_1) or lower(column_1), as appropriate, this may force a full table scan. In order to avoid this you can create a function-based index.

create index my_index on my_table ( lower(column_1) );

If you're using LIKE then you have to concatenate a % around the string you're searching for.

select * from my_table where lower(column_1) LIKE lower('my_string') || '%';

This SQL Fiddle demonstrates what happens in all these queries. Note the Explain Plans, which indicate when an index is being used and when it isn't.

2. Use regular expressions.

From Oracle 10g onwards REGEXP_LIKE() is available. You can specify the _match_parameter_ 'i', in order to perform case-insensitive searching.

In order to use this as an equality operator you must specify the start and end of the string, which is denoted by the carat and the dollar sign.

select * from my_table where regexp_like(column_1, '^my_string$', 'i');

In order to perform the equivalent of LIKE, these can be removed.

select * from my_table where regexp_like(column_1, 'my_string', 'i');

Be careful with this as your string may contain characters that will be interpreted differently by the regular expression engine.

This SQL Fiddle shows you the same example output except using REGEXP_LIKE().

3. Change it at the session level.

The NLS_SORT parameter governs the collation sequence for ordering and the various comparison operators, including = and LIKE. You can specify a binary, case-insensitive, sort by altering the session. This will mean that every query performed in that session will perform case-insensitive parameters.

alter session set nls_sort=BINARY_CI

There's plenty of additional information around linguistic sorting and string searching if you want to specify a different language, or do an accent-insensitive search using BINARY_AI.

You will also need to change the NLS_COMP parameter; to quote:

The exact operators and query clauses that obey the NLS_SORT parameter depend on the value of the NLS_COMP parameter. If an operator or clause does not obey the NLS_SORT value, as determined by NLS_COMP, the collation used is BINARY.

The default value of NLS_COMP is BINARY; but, LINGUISTIC specifies that Oracle should pay attention to the value of NLS_SORT:

Comparisons for all SQL operations in the WHERE clause and in PL/SQL blocks should use the linguistic sort specified in the NLS_SORT parameter. To improve the performance, you can also define a linguistic index on the column for which you want linguistic comparisons.

So, once again, you need to alter the session

alter session set nls_comp=LINGUISTIC

As noted in the documentation you may want to create a linguistic index to improve performance

create index my_linguistc_index on my_table 
   (NLSSORT(column_1, 'NLS_SORT = BINARY_CI'));
Sepster
  • 4,800
  • 20
  • 38
Ben
  • 51,770
  • 36
  • 127
  • 149
  • 1
    "create a function-based index" Amazing what a difference this can make – Jacob Goulden Jun 04 '15 at 18:45
  • 3
    May I ask why it is different to do `select * from my_table where lower(column_1) LIKE lower('my_string') || '%';` instead of `select * from my_table where lower(column_1) LIKE lower('my_string%');` ? Does it give any advantage? – lopezvit Jan 25 '16 at 10:04
  • 3
    One reason would be if your query is paramerterized (likely in most situations) then your calling code doesn't need to always concatenate a % on the end @lopezvit. – Ben Jan 25 '16 at 11:51
  • 2
    If there are some characters that will mess-up the result of `regexp_like`, is there a way to escape such strings? Giving an example, if the string has $, the output will be not as what we expect. //cc @Ben and others please do share. – bozzmob Jul 11 '16 at 08:02
  • 4
    `\`` [is the escape character](http://docs.oracle.com/database/121/SQLRF/ap_posix001.htm#SQLRF55540) @bozzmob. There should be no difference in output if the string the regular expression is operating on contains a `$`, this may only cause you problems if you need a `$` literal in your regular expression. If you've got a specific issue I'd ask another question if this comment/answer hasn't helped. – Ben Jul 11 '16 at 12:11
  • 1
    I want to add more in the first way. Lets say, a record like **I'm Bob** exists, then it wont work as it would search for the keyword **Bob** in the starting and wont return that value. So to search the keyword anywhere in the record one can use, **select * from my_table where lower(column_1) LIKE '%' || lower('my_string') || '%';**. – Yashwin Munsadwala Jan 09 '19 at 15:30
  • 1
    At that point you'll never use indexes @Yashwin. Depending on the volume of your data you might want to consider other options, such as full text indexes. – Ben Jan 09 '19 at 18:17
  • 1
    Which is faster? Lower to lower or upper to upper comparison? – Fseee Oct 02 '19 at 13:07
  • 1
    I'd be very surprised if it made any difference @Franky. You can create your own comparison if you're very worried but I'd focus on the SQL and indexing rather than the `UPPER()` or `LOWER()` fuctions. – Ben Oct 02 '19 at 19:29
87

Since 10gR2, Oracle allows to fine-tune the behaviour of string comparisons by setting the NLS_COMP and NLS_SORT session parameters:

SQL> SET HEADING OFF
SQL> SELECT *
  2  FROM NLS_SESSION_PARAMETERS
  3  WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

NLS_SORT
BINARY

NLS_COMP
BINARY


SQL>
SQL> SELECT CASE WHEN 'abc'='ABC' THEN 1 ELSE 0 END AS GOT_MATCH
  2  FROM DUAL;

         0

SQL>
SQL> ALTER SESSION SET NLS_COMP=LINGUISTIC;

Session altered.

SQL> ALTER SESSION SET NLS_SORT=BINARY_CI;

Session altered.

SQL>
SQL> SELECT *
  2  FROM NLS_SESSION_PARAMETERS
  3  WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

NLS_SORT
BINARY_CI

NLS_COMP
LINGUISTIC


SQL>
SQL> SELECT CASE WHEN 'abc'='ABC' THEN 1 ELSE 0 END AS GOT_MATCH
  2  FROM DUAL;

         1

You can also create case insensitive indexes:

create index
   nlsci1_gen_person
on
   MY_PERSON
   (NLSSORT
      (PERSON_LAST_NAME, 'NLS_SORT=BINARY_CI')
   )
;

This information was taken from Oracle case insensitive searches. The article mentions REGEXP_LIKE but it seems to work with good old = as well.


In versions older than 10gR2 it can't really be done and the usual approach, if you don't need accent-insensitive search, is to just UPPER() both the column and the search expression.

Álvaro González
  • 142,137
  • 41
  • 261
  • 360
  • 1
    This works well, but it makes the UPDATES using the LIKE / = operators very slow...... :( – Saqib Ali Jul 29 '15 at 22:42
  • 1
    @SaqibAli Arbitrary `LIKE` expressions (e.g. `WHERE foo LIKE '%abc%'`) are already slow enough if they can't be indexed, I don't think it's specifically related to case sensitiveness. – Álvaro González Jul 30 '15 at 06:19
  • 1
    You can also set these outside of SQLPLUS, like in the shell environment. For example in a Perl script using `DBD::Oracle`, you can write `$ENV{NLS_SORT} = 'BINARY_CI'; $ENV{NLS_COMP} = 'LINGUISTIC';` before calling ` DBI->connect`. – mivk Feb 08 '17 at 12:22
  • hey does the `ALTER SESSION` only alter your local instance of the correction and does it mean like your current session i.e. if i close and reopen it would have reset. Is there a way that i can see what the current values are so that if its persisted everywhere i can change back to original settings... – Seabizkit Jun 13 '17 at 14:40
56

maybe you can try using

SELECT user_name
FROM user_master
WHERE upper(user_name) LIKE '%ME%'
V4Vendetta
  • 37,194
  • 9
  • 78
  • 82
  • 5
    it works when input parameter is whole upper-case ,and if lower or mixed it doesn't – sergionni Mar 22 '11 at 12:27
  • 19
    Have you thought about `WHERE upper(user_name) LIKE UPPER('%ME%')` then? :) – Konerak Mar 22 '11 at 12:27
  • 4
    @sergionni you must upper case the search term as well! – Markus Winand Mar 22 '11 at 12:28
  • 4
    @sergionni, well then why don't you use `UPPER` on the input parameter too? – Czechnology Mar 22 '11 at 12:28
  • 1
    @sergionni you should also [consider a function based index for performance](http://use-the-index-luke.com/sql/where-clause/functions/case-insensitive-search). – Markus Winand Mar 22 '11 at 12:30
  • 1
    @Markus Winand: it is leading `%`. So index will not be used. – zerkms Mar 22 '11 at 12:30
  • 1
    @sergionni: There is no relevance of upper for the expression since i am already saying that in CAPS **LIKE '%ME%'**, so wonder how will upper of this transform to anything special, unless you are passing the expression as parameter and you wern't controlling its case – V4Vendetta Mar 22 '11 at 15:55
  • 1
    @V4Vendetta: part of StackOverflow is not only reading what it says, but guessing what it means. The `%ME%` probably comes from a variable somewhere, but the author didn't mention that ;) You are offcourse right, if the `%ME%` is literally like that in the where clause, `UPPER(%ME%) == %ME%` so there is no difference. – Konerak Mar 22 '11 at 19:30
  • 8
    @V4Vendetta using the `upper` function you lose the index, do you have any idea how to make search using the index? – jcho360 Feb 28 '13 at 18:57
  • 2
    This is going to be **horribly slow** for large record sets that does not have function based indexes. – sampathsris Sep 12 '14 at 07:11
16

From Oracle 12c R2 you could use COLLATE operator:

The COLLATE operator determines the collation for an expression. This operator enables you to override the collation that the database would have derived for the expression using standard collation derivation rules.

The COLLATE operator takes one argument, collation_name, for which you can specify a named collation or pseudo-collation. If the collation name contains a space, then you must enclose the name in double quotation marks.

Demo:

CREATE TABLE tab1(i INT PRIMARY KEY, name VARCHAR2(100));

INSERT INTO tab1(i, name) VALUES (1, 'John');
INSERT INTO tab1(i, name) VALUES (2, 'Joe');
INSERT INTO tab1(i, name) VALUES (3, 'Billy'); 
--========================================================================--
SELECT /*csv*/ *
FROM tab1
WHERE name = 'jOHN' ;
-- no rows selected

SELECT /*csv*/ *
FROM tab1
WHERE name COLLATE BINARY_CI = 'jOHN' ;
/*
"I","NAME"
1,"John"
*/

SELECT /*csv*/ *
FROM tab1 
WHERE name LIKE 'j%';
-- no rows selected

SELECT /*csv*/ *
FROM tab1 
WHERE name COLLATE BINARY_CI LIKE 'j%';
/*
"I","NAME"
1,"John"
2,"Joe"
*/

db<>fiddle demo

Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275
5

The COLLATE operator also works if you put it at the end of the expression, and that seems cleaner to me. So you can use this:

WHERE name LIKE 'j%' COLLATE BINARY_CI 

instead of this:

WHERE name COLLATE BINARY_CI LIKE 'j%'

Anyhow, I like the COLLATE operator solution for the following reasons:

  • you put it only once in the expression and you don't need to worry about multiple UPPER or LOWER, and where to put them
  • it is isolated to the exact statement and expression where you need it, unlike ALTER SESSION solution that makes it applicable to everything. And your query will work consistently regardless of the DB or session NLS_SORT setting.
Milan
  • 91
  • 1
  • 2
  • `COLLATE` is a postfix unary operator ([source](https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/COLLATE-Operator.html)) so it applies to the string that precedes it. I don't know the rules for `=` when dealing with different collations. – Álvaro González Feb 21 '22 at 16:56
2

you can do something like that:

where regexp_like(name, 'string$', 'i');
grep
  • 5,465
  • 12
  • 60
  • 112
2
select user_name
from my_table
where nlssort(user_name, 'NLS_SORT = Latin_CI') = nlssort('%AbC%', 'NLS_SORT = Latin_CI')
Clodoaldo Neto
  • 118,695
  • 26
  • 233
  • 260