The query is the lever
Before adding an index, ask: is the query asking the database to do too much? SELECT * when only three columns are needed, joins on tables you don't actually use, ORDER BY before LIMIT when the order is irrelevant — all of these are query-shape problems no index can fix.
The classic shape problems
- Selecting columns you don't use (extra IO, defeats covering indexes).
- Joining tables you don't filter or select from.
- Aggregating before joining when you could aggregate after.
- Computing expensive expressions in the SELECT list when they could be cached.
- Pulling 10,000 rows back to the application to "find the right one" — let the database do the filtering.
The right order of optimisation
- Fix the query shape — narrow the columns, narrow the rows, push work into SQL.
- Then add the index — if EXPLAIN still shows a problem.
- Then add the cache — only if the data is read often and changes rarely.
- Then add the materialised view — only if the aggregation is genuinely expensive.