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:
- Configuring PostgreSQL for read performance
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