Postgresql Query getting slow execution time

postgresqlrails

I did the explain analyze for this query, it was giving 30ms, but if the data is more I will get an execution expired; Using PostgreSQL 10

For normal execution: https://explain.depesz.com/s/gSPP

For slow execution: https://explain.depesz.com/s/bQN2

SELECT inventory_histories.*, order_items.order_id as order_id FROM
"inventory_histories" LEFT JOIN order_items ON (order_items.id = 
inventory_histories.reference_id AND inventory_histories.reference_type = 4) 
WHERE "inventory_histories"."inventory_id" = 1313 AND 
(inventory_histories.location_id = 15) ORDER BY inventory_histories.id DESC 
LIMIT 10 OFFSET 0;

Indexes:

"inventory_histories_pkey" PRIMARY KEY, btree (id)
"inventory_histories_created_at_index" btree (created_at)
"inventory_histories_inventory_id_index" btree (inventory_id)
"inventory_histories_location_id_index" btree (location_id)

Best Answer

At the key node, it is expecting 83 rows but finding 46,547. Unless your statistics are way out of date, that means there is a correlation between inventory_id and location_id. You could try to CREATE STATISTICS on dependencies between those two columns, but it not really clear what it would do instead if it had correct estimates. Maybe a hash join to the order_items table, rather than poorly cached nested loop, but that would depend on how big the table is.

A more direct route to address this would be to create an index on (inventory_id, location_id, id DESC). This would allow it to jump in the index to the correct values of inventory_id and location_id, and walk it in "id" order until it collects 10 rows. The "DESC" is probably not necessary, you could try without it and see if it works. You could also add "reference_type" to the index just before "id", but it would only be a minor improvement and might also make the index less general for application to other queries.