627

Is there any way to write case-insensitive queries in PostgreSQL, E.g. I want that following 3 queries return same result.

SELECT id FROM groups where name='administrator'

SELECT id FROM groups where name='ADMINISTRATOR'

SELECT id FROM groups where name='Administrator'
3
  • 1
    if citext comes with your Postgres installation, try citext type. It's case-insensitive text Commented Aug 10, 2011 at 4:06
  • 7
    For newcomers to this question, this link to official postgres documentation contains all the answers given here, as well as a few other options. Commented Jul 7, 2014 at 20:38
  • 2
    Sir reassign accepted answer to the one made by @Arun please. It is much less complicated and do not pull bunch of troubles after apply. Commented Jun 7, 2016 at 17:25

17 Answers 17

746

Use LOWER function to convert the strings to lower case before comparing.

Try this:

SELECT id 
  FROM groups
 WHERE LOWER(name)=LOWER('Administrator')
Sign up to request clarification or add additional context in comments.

16 Comments

It's important to note that using LOWER (or any function) on the predicate columns--in this case "name"--will cause any indexes to no longer be seekable. If this is a large or frequently queried table, that could cause trouble. Case-insensitive collation, citext, or a function-based index will improve performance.
Or just create an index like this: CREATE INDEX idx_groups_name ON groups lower(name);
Also specify varchar_pattern_ops if you want the index to work with LIKE 'xxx%' query, i.e. CREATE INDEX ix_groups_name ON groups (lower(name) varchar_pattern_ops).
Using the ILIKE operator (as shown in other answers below) is a simpler approach, even though this is the most voted answer.
Going through the comments here, a lot of suggestions here suggests ILIKE, It will work, but with slow response. To obtain fast access to tables based on the results of computations, I suggest anyone just checking this should go with the accepted answer. See more details here and here
|
603

using ILIKE instead of LIKE

SELECT id FROM groups WHERE name ILIKE 'Administrator'

6 Comments

Note that ILIKE is not supported by Hibernate when used in Spring Boot.
@AnT it works with org.hibernate.dialect.PostgreSQL94Dialect and Spring Boot 2.0.6.RELEASE. But IntelliJ complains about it.
This is the actual answer, the accepted one is a hack
Actually, ILIKE is the simplest answer but not the "actual" answer for all cases. ILIKE is a non-standard extension to Postgres and it will perform very slowly. The accepted answer using the LOWER function, along with proper indexing, will perform much better and will be supported by all client libraries and ORMS. It depends on your particular use case.
i have table with 100k rows.. diff between name ILIKE 'Administrator' and LOWER(name)=LOWER('Administrator') is 5000ms (tried gist and gin trgm indexes, similar result) vs 5ms(btree lower index).. DO NOT USE THIS
|
186

The most common approach is to either lowercase or uppercase the search string and the data. But there are two problems with that.

  1. It works in English, but not in all languages. (Maybe not even in most languages.) Not every lowercase letter has a corresponding uppercase letter; not every uppercase letter has a corresponding lowercase letter.
  2. Using functions like lower() and upper() will give you a sequential scan. It can't use indexes. On my test system, using lower() takes about 2000 times longer than a query that can use an index. (Test data has a little over 100k rows.)

There are at least three less frequently used solutions that might be more effective.

  1. Use the citext module, which mostly mimics the behavior of a case-insensitive data type. Having loaded that module, you can create a case-insensitive index by CREATE INDEX ON groups (name::citext);. (But see below.)
  2. Use a case-insensitive collation. This is set when you initialize a database. Using a case-insensitive collation means you can accept just about any format from client code, and you'll still return useful results. (It also means you can't do case-sensitive queries. Duh.)
  3. Create a functional index. Create a lowercase index by using CREATE INDEX ON groups (LOWER(name));. Having done that, you can take advantage of the index with queries like SELECT id FROM groups WHERE LOWER(name) = LOWER('ADMINISTRATOR');, or SELECT id FROM groups WHERE LOWER(name) = 'administrator'; You have to remember to use LOWER(), though.

The citext module doesn't provide a true case-insensitive data type. Instead, it behaves as if each string were lowercased. That is, it behaves as if you had called lower() on each string, as in number 3 above. The advantage is that programmers don't have to remember to lowercase strings. But you need to read the sections "String Comparison Behavior" and "Limitations" in the docs before you decide to use citext.

18 Comments

About #1: It shouldn't be a problem, since it would be two different strings (think of it like doing col = 'a' and col = 'b'). About #2: As you said, you can create an index on an expression, so it's not really a problem. But I agree with you that changing the collation is most likely the best solution.
Can someone tell me what case-insensitive collations are PostgreSQL built-in collations ? I see this as an option but can't find anything about a case-insensitive collation for Postgres on the net ?
@AnupShah: No, I'm not saying that. I'm not running PostgreSQL on Windows. The 9.4 docs say this: "On all platforms, the collations named default, C, and POSIX are available. Additional collations may be available depending on operating system support." You can see which collations PostgreSQL thinks are available with select * from pg_collation;.
@Matthieu: This is best introduction (and caution) to the subject that I know about: Edge Cases to Keep in Mind. Part 1 – Text.
|
143

You can use ILIKE. i.e.

SELECT id FROM groups where name ILIKE 'administrator'

3 Comments

Its correct and working fine for me, I am using MAC OS X(Mountain Lion).
This will work, but with slow response. To obtain fast access to tables based on the results of computations, I suggest to use the lower function. See more details
@AfolabiOlaoluwaAkinwumi fundamentally this comes down to whether you're searching for results opposed to filtering known values. In the latter case, a single uniform case should be persisted at the data level allowing the equality operator to work. [Personal recommendation is upper pascal case for type code values]
62

You can also read up on the ILIKE keyword. It can be quite useful at times, albeit it does not conform to the SQL standard. See here for more information: http://www.postgresql.org/docs/9.2/static/functions-matching.html

8 Comments

Something to watch out for here is malicious user input. If you run a query like email ILIKE 'user-input-email-here', make sure to escape the user input. Otherwise people can enter characters like % that match anything.
@MattDeLeon Hi. Well said. But I just want to ask you, if I use ILIKE and prepared statements will this protect me from sql injection?
"The key word ILIKE can be used instead of LIKE to make the match case-insensitive according to the active locale. This is not in the SQL standard but is a PostgreSQL extension." Works like a charm in 9.3
ILIKE is slower than lower(column_name) like %expression%.
@PatrykImosa: Can you please elaborate or show an example of ILIKE being slower?
|
42

You could also use POSIX regular expressions, like

SELECT id FROM groups where name ~* 'administrator'

SELECT 'asd' ~* 'AsD' returns t

5 Comments

I had the same problem, I needed case insensitive searches on my PostgreSQL database. I thought about transforming the user input string into a regular expression. Now, using ~* instead of = or LIKE worked perfectly! I didn't need to create new indexes, columns or whatever. Sure, regex search is slower than straight byte comparison, but I don't think the impact on performance would be so much greater than having to handle two sets of data (one lower or uppercased just for searching, then having to retrieve the corresponding original data from the other set). Besides, this is cleaner!
Fine, but how to do with regexp_matches() for example ?
According to postgres docs: The operator ~~ is equivalent to LIKE, and ~~* corresponds to ILIKE. There are also !~~ and !~~* operators that represent NOT LIKE and NOT ILIKE, respectively. All of these operators are PostgreSQL-specific.
I faced a issue when brackets are included in the text, its not working. like: "code (LC)"
faced issues with special characters.
37

use ILIKE

select id from groups where name ILIKE 'adminstration';

If your coming the expressjs background and name is a variable use

select id from groups where name ILIKE $1;

2 Comments

what's the performance tradeoff between direct matching and ILIKE ?
The idea is to make sure not you match every string variations, To get to your point, The time and space are the same, However ILIKE does a deep comparision
16

Using ~* can improve greatly on performance, with functionality of INSTR.

SELECT id FROM groups WHERE name ~* 'adm'

return rows with name that contains OR equals to 'adm'.

2 Comments

Hey, Robin, welcome to SO. James Brown's answer already proposed this solution. Additionally, your proposed answer does not leverage regex in any way.
~* is not precisely case-insensitive match. It is regex pattern matching. In your example if db contains ADM then where name ~* 'Adm' or where name ~* 'Ad' would yield results. Use ILIKE instead
12

ILIKE work in this case:

SELECT id 
  FROM groups
 WHERE name ILIKE 'Administrator'

Comments

11

A tested approach is using ~*

As in the example below

SELECT id FROM groups WHERE name ~* 'administrator'

1 Comment

This answer doesn't seem to work for all kinds of strings, if the needle contains a *, it never seems to return any results
7

None of the existing answers are correct.

The Turkish language has multiple representations of the letter I which are equal from a case-sensitivity point of view. Comparing the lowercase or uppercase forms of these characters will return false.

Georgian has a character that has no uppercase equivalent, and a character that has no lowercase equivalent. If you dig into Java's implementation of String.equalsIgnoreCase() you will find the following code snippet:

// Case insensitive comparison of two code points
private static int compareCodePointCI(int cp1, int cp2) {
    // try converting both characters to uppercase.
    // If the results match, then the comparison scan should
    // continue.
    cp1 = Character.toUpperCase(cp1);
    cp2 = Character.toUpperCase(cp2);
    if (cp1 != cp2) {
        // Unfortunately, conversion to uppercase does not work properly
        // for the Georgian alphabet, which has strange rules about case
        // conversion.  So we need to make one last check before
        // exiting.
        cp1 = Character.toLowerCase(cp1);
        cp2 = Character.toLowerCase(cp2);
        if (cp1 != cp2) {
            return cp1 - cp2;
        }
    }
    return 0;
}

Besides, if you'd like to compare strings in a case-insensitive manner you'd likely want to strip out other differences as well. You can use a process called "text normalization" to convert text into a canonical form that strips away accents, punctuation, characters that display the same but have different values, and invisible characters.

The way I personally dealt with this problem is storing two representations for each column that I want to search against:

  • foobar_display which corresponds to the original value that the user entered, and is used for display purposes.
  • foobar_compare which is used for search and comparison purposes.

Every time that I insert a row into the database I convert foobar_display into foobar_compareusing a [text normalization library](https://github.com/slugify/slugify). I store both values, and any time a user tries searching for a value I usefoobar_compare` to detect partial or full matches.

Finally, I add a unique constraint against foobar_compare to unwanted duplicates.

Comments

4

If you want not only upper/lower case but also diacritics, you can implement your own func:

CREATE EXTENSION unaccent;

CREATE OR REPLACE FUNCTION lower_unaccent(input text)
 RETURNS text
 LANGUAGE plpgsql
AS $function$
BEGIN
    return lower(unaccent(input));
END;
$function$;

Call is then

select lower_unaccent('Hôtel')
>> 'hotel'

Comments

3

For a case-insensitive parameterized query, you can use the following syntax:

 "select * from article where upper(content) LIKE upper('%' || $1 || '%')"

Comments

3
-- Install 'Case Ignore Test Extension'
create extension citext;

-- Make a request
select 'Thomas'::citext in ('thomas', 'tiago');

select name from users where name::citext in ('thomas', 'tiago');

Comments

2

For complex queries beyond this simple field = value I suggest you use case insensitive nondeterminstic collation and applying it to your query

CREATE COLLATION case_insensitive (provider = icu, locale = 'und-u-ks-level2', deterministic = false);

Then to use it

SELECT id FROM groups where name='administrator' COLLATE case_insensitive;

See full docs https://www.postgresql.org/docs/current/collation.html#COLLATION-NONDETERMINISTIC

Comments

0

How to :

  1. install this bundle : composer require martin-georgiev/postgresql-for-doctrine

  2. add config in doctrine.yaml file

# app/config/packages/doctrine.yaml
doctrine:
    orm:
        dql:
            string_functions:
                ILIKE: MartinGeorgiev\Doctrine\ORM\Query\AST\Functions\Ilike
  1. clear your doctrine cache like that :
 php bin/console doctrine:cache:clear-metadata 
 php bin/console doctrine:cache:clear-query  
 php bin/console doctrine:cache:clear-result
  1. define your queries like that :
$term = "Some search text";

$query->where('ILIKE(p.column, :term) = true')
    ->setParameter('value', '%' . $term . '%');

Enjoy !

Comments

-21
select id from groups where name in ('administrator', 'ADMINISTRATOR', 'Administrator')

1 Comment

Please add some explanation to your answer such that others can learn from it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.