Search code examples
postgresqlquery-performance

Efficient way to find ordered string's exact, prefix and postfix match in PostgreSQL


Given a table name table and a string column named column, I want to search for the word word in that column in the following way: exact matches be on top, followed by prefix matches and finally postfix matches.

Currently I got the following solutions:

Solution 1:

select column
from (select column,
             case
                 when column like 'word' then 1
                 when column like 'word%' then 2
                 when column like '%word' then 3
                 end as rank
      from table) as ranked
where rank is not null
order by rank;

Solution 2:

select column
from table
where column like 'word'
   or column like 'word%'
   or column like '%word'
order by case
             when column like 'word' then 1
             when column like 'word%' then 2
             when column like '%word' then 3
             end;

Now my question is which one of the two solutions are more efficient or better yet, is there a solution better than both of them?


Solution

  • Your 2nd solution looks simpler for the planner to optimize, but it is possible that the first one gets the same plan as well.

    For the Where, is not needed as it is covered by ; it might confuse the DB to do 2 checks instead of one.

    But the biggest problem is the third one as this has no way to be optimized by an index.

    So either way, PostgreSQL is going to scan your full table and manually extract the matches. This is going to be slow for 20,000 rows or more.

    I recommend you to explore fuzzy string matching and full text search; looks like that is what you're trying to emulate.

    Even if you don't want the full power of FTS or fuzzy string matching, you definitely should add the extension "pgtrgm", as it will enable you to add a GIN index on the column that will speedup LIKE '%word' searches.

    https://www.postgresql.org/docs/current/pgtrgm.html

    And seriously, have a look to FTS. It does provide ranking. If your requirements are strict to what you described, you can still perform the FTS query to "prefilter" and then apply this logic afterwards.

    There are tons of introduction articles to PostgreSQL FTS, here's one: https://www.compose.com/articles/mastering-postgresql-tools-full-text-search-and-phrase-search/

    And even I wrote a post recently when I added FTS search to my site: https://deavid.wordpress.com/2019/05/28/sedice-adding-fts-with-postgresql-was-really-easy/