Finding the Missing Index with EXPLAIN ANALYZE

A 4-second query that looked innocent in staging — sequential scan on 40M rows in production.

Staging had 50K orders. Production had 40M. Same query, different planet.

The Query

SELECT * FROM orders
WHERE tenant_id = $1 AND status = 'pending'
ORDER BY created_at DESC
LIMIT 50;

EXPLAIN (ANALYZE, BUFFERS)

Seq Scan on orders  (cost=0..892341 rows=120000 width=...)
  Filter: ((tenant_id = 1) AND (status = 'pending'))
  Rows Removed by Filter: 39880000
  Buffers: shared hit=450000 read=120000
Execution Time: 4231.442 ms

Fix

CREATE INDEX CONCURRENTLY idx_orders_tenant_status_created
ON orders (tenant_id, status, created_at DESC);

Execution time: 4.2s → 12ms.

Always test query plans against production-scale stats, not empty staging DBs.

postgresql , indexes , query-planning · PostgreSQL

More in PostgreSQL