Why your Postgres index is not being used

Four reasons the planner walks straight past the index you added, and how to check which one it is.

Lines of code on a laptop screen

You added an index. The query is still slow. Before you add another one, find out why the planner is ignoring the first.

Start with EXPLAIN

EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE lower(email) = 'a@b.co';

If you see Seq Scan, keep reading.

1. The query wraps the column in a function

An index on email cannot help lower(email). Either index the expression or store the value already lowered.

2. The types do not match

Comparing a text column to a number makes Postgres cast every row. Cast the parameter instead.

3. The planner thinks a scan is cheaper

On a small table, or when most rows match, reading everything is genuinely faster. Check the row estimates against reality.

4. The statistics are stale

After a big import, run ANALYZE on the table so the planner knows what it is dealing with.

Spotted a mistake?

Tell me and I will fix the post

Corrections are credited at the bottom of the article.