Extremely slow query on indexed column in Postgres

There is a subtle problem hidden in your ORDER BY clause:

ORDER BY updated_at DESC 

Would sort NULL values first. I assume you do not want that. Your column updated_at can be NULL (lacks a NOT NULL constraint). The missing constraint should probably be added. Your query should be fixed either way:

SELECT * 
FROM   orders 
WHERE  shop_id = 3828 
ORDER  BY updated_at DESC NULLS LAST
LIMIT  1;

And the multicolumn index @Ste Bov already mentioned should be adapted accordingly:

CREATE INDEX orders_shop_id_updated_at_idx ON orders (shop_id, updated_at DESC NULLS LAST);

Then you get a basic Index Scan instead of the (almost as fast) Index Scan Backward, and you won't get an additional index condition: Index Cond: (updated_at IS NOT NULL) that you would get without the added NULLS LAST.

Related:

  • Unused index in range of dates query

Asides

You can save a bit of wasted disk space by optimizing the sequence of columns for your big table (which makes everything a bit faster):

id                 | integer                     | not null default nextval( ...
shop_id            | integer                     |
sent               | boolean                     | default false
name               | varchar(255)                |
total_price        | double precision            |
recovered_at       | timestamp without time zone |
created_at         | timestamp without time zone |
updated_at         | timestamp without time zone |

Details:

And add NOT NULL constraints to all columns that cannot be NULL.
And consider text instead of varchar(255), timestamptz instead of timestamp and integer for the price (as Cent) - or, if you rather want to store fractional numbers, use numeric which is an arbitrary precision type and stores your values exact as given. Never use a lossy floating point type for a "price" or anything to do with money.


I dont know Postgresql too well, but you're checking across two seperate keys to find the values you're looking for, try creating it as a composite key instead

"index_orders_on_shop_id" btree (shop_id)
"index_orders_on__updated_at" btree (updated_at)

becomes

"index_orders_on_shop_id__updated_at" btree (shop_id,updated_at)

that could help

if there's a way to include values in an index that would work even better