SQL Query Performance: Indexing Checklist
Thu Aug 14 2025
Indexes are powerful but misapplied very often. Follow a disciplined flow.
1. Capture the Slow Query
- Log exact text (no prettifying).
- Record average latency + P95.
- Count executions per minute.
2. EXPLAIN / EXPLAIN ANALYZE
EXPLAIN ANALYZE SELECT ...;
Focus on:
- Sequential scans on large tables
- Rows vs rows removed by filter
- Join strategy (hash vs nested loop)
3. Check Existing Indexes
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'users';
Guard against redundant indexes increasing write cost.
4. Filter & Join Columns First
Add composite indexes in left-to-right order of selectivity.
5. Cover the Query
Include needed columns (Postgres: INCLUDE clause) to avoid heap lookups.
6. Avoid Over-Indexing
Each index adds write amplification and memory use. Measure insert/update cost after adding.
7. Partial Indexes
Useful when most rows share a default and you query the minority:
CREATE INDEX CONCURRENTLY idx_users_active ON users (last_login) WHERE active = true;
8. Vacuum / Analyze Health
Check bloat:
SELECT relname, n_dead_tup FROM pg_stat_user_tables ORDER BY n_dead_tup DESC LIMIT 5;
9. Re-Measure
Compare P95 before vs after. Keep a changelog of index additions.
10. Remove Unused Indexes
Track usage:
SELECT relname, idx_scan FROM pg_stat_user_indexes WHERE idx_scan = 0;
Intentional indexing = predictable performance.