How I approach a slow SQL query
2025-07-28
When a SQL query is slow, I don’t jump straight to indexing or rewriting. First, I step back and ask:
📌 Is this query always slow? 📌 Or does it degrade under load?
That tells me whether I’m chasing a design issue or a scaling issue. From there, here’s the process I follow 👇
-
Run EXPLAIN (ANALYZE) This is home base. I’m looking for full table scans, nested loops, row misestimates, and I/O-heavy operations. It tells me where the real cost lives.
-
If it’s scanning too much, check indexing Missing or misused indexes are often the low-hanging fruit—especially on WHERE, JOIN, or ORDER BY.
-
If row counts look huge, check filtering Can I apply filters earlier? Push conditions closer to the base tables?
-
If the query joins multiple big tables, simplify Sometimes restructuring or denormalizing a bit is worth the tradeoff.
-
If I see repeated queries inside loops, it’s probably N+1 This usually comes from lazy loading in ORMs. I preload or rewrite to batch instead.
-
If a lot of unnecessary columns are being fetched, I reduce the payload But only after confirming it’s part of the problem. SELECT * isn’t always bad—it depends on what’s being returned and why.
-
If the query is still slow but the data doesn’t change often, I consider caching This can be a game-changer—but only when staleness is acceptable and cache invalidation is manageable.
-
For high-volume or time-based data, I look at partitioning PostgreSQL partitioning works best when queries align with the partition key (e.g. time windows). Otherwise, it can add complexity with no gain.
-
If this query runs fine alone, but collapses under concurrency, it’s time to look at connection pooling, locks, and transaction scopes Sometimes the query isn’t the problem—the context is.
💡 These aren’t just tips, they’re questions I ask the system. Query performance isn’t solved with a checklist. It’s diagnosed.