Order of multi column indexes can make an impact:
Having this multi-column index:
CREATE INDEX idx_name on users(first_name, last_name)
I want to find users by last name but there is no index:
explain (costs false)
select * from users where last_name = 'Doe'
QUERY PLAN
---------------------------------------------
Seq Scan on users
Filter: ((last_name)::text = 'Doe'::text)
Instead of making an index on last_name, you can re-arrange
the ordering of the index like:
CREATE INDEX idx_name on users(last_name, first_name)
and then the index will be used in the query:
explain (costs false)
select * from users where last_name = 'Doe'
QUERY PLAN
-------------------------------------------------
Index Scan using idx_name on users
Index Cond: ((last_name)::text = 'Doe'::text)
I want to make a case insensitive search by last name:
explain (costs false)
select * from users where LOWER(last_name) = LOWER('Doe')
QUERY PLAN
---------------------------------------------
Seq Scan on users
Filter: (lower((last_name)::text) = 'doe'::text)
You can create index on expressions:
CREATE INDEX idx_lower_name on users(LOWER(last_name))
and then:
explain (costs false)
select * from users where LOWER(last_name) = LOWER('Doe')
QUERY PLAN
--------------------------------------------------------
Index Scan using idx_lower_name on users
Index Cond: (lower((last_name)::text) = 'doe'::text)
If you want case insensitive text, you should look at the Citext type (https://www.postgresql.org/docs/current/citext.html).
Don't do it ... You probably want a partial index:
CREATE INDEX idx_active_names on users(last_name) WHERE active = true
Indexes support prefix matching:
explain (costs false)
select * from users where last_name LIKE 'Rodr%'
QUERY PLAN
-------------------------------------------------
Index Scan using idx_last_name on users
Index Cond: ((last_name)::text ~~ 'Rodr%'::text)
Unfortunately, not suffix matching:
explain (costs false)
select * from users where last_name LIKE '%guez'
QUERY PLAN
-------------------------------------------------
Seq Scan on users
Filter: ((last_name)::text ~~ '%guez'::text)
But there is a trick:
CREATE INDEX backsearch ON users (reverse(last_name))
This makes it look like a prefix match:
explain (costs false)
select * from users where reverse(last_name) LIKE reverse('%guez')
QUERY PLAN
-------------------------------------------------
Index Scan using idx_last_name on users
Index Cond: (reverse((last_name)::text) ~~ 'zeug%'::text)
Kappa.
Book: https://use-the-index-luke.com
Reference: https://www.postgresql.org/docs/current/indexes.html
Postgres MVCC: https://malisper.me/postgres-mvcc/