How to Fix an N+1 Query

An N+1 query fetches a list with one query, then fires a separate query for each row - 101 queries to load 100 orders. It's the most common hidden cause of a slow endpoint. Here's how to spot and fix it.

Backend Engineerpythonpostgresqlsql

What an N+1 query is

You query a list (1 query), then loop over the results and query a related record for each row (N queries). For 100 orders that's 1 + 100 = 101 round-trips to the database - each one a network hop. The endpoint feels slow for no obvious reason because no single query is slow; there are just too many of them.

# BAD: N+1 - one query for orders, then one per order for its user
cur.execute("SELECT id, user_id, product FROM orders")
for order in cur.fetchall():
    cur.execute("SELECT name, email FROM users WHERE id = %s", (order[1],))  # +1 each
    user = cur.fetchone()

How to spot it

Fix 1: a single JOIN

Let the database do the join once:

cur.execute("""
    SELECT o.id, o.product, u.name, u.email
    FROM orders o
    JOIN users u ON u.id = o.user_id
""")
rows = cur.fetchall()   # 1 query, all the data

101 queries become 1.

Fix 2: batch with IN (when a JOIN doesn't fit)

Collect the ids and fetch them all at once, then stitch in memory:

user_ids = {o["user_id"] for o in orders}
cur.execute("SELECT id, name, email FROM users WHERE id = ANY(%s)", (list(user_ids),))
users = {u[0]: u for u in cur.fetchall()}     # 2 queries total

In an ORM

Use eager loading instead of lazy access in a loop:

Why it matters

N+1 is the single most common reason a "simple" endpoint is slow. The fix is almost always free - one JOIN or one batched query - and it scales: the JOIN stays one query whether there are 10 rows or 10,000.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

What is an N+1 query problem?

You run one query to load a list, then one extra query per row to load related data - 101 queries for 100 rows. The many round-trips, not any single slow query, make the endpoint slow.

How do I fix an N+1 query?

Replace the per-row queries with a single JOIN, or batch the lookups into one WHERE id IN (...) query and stitch the data in memory. In an ORM, use eager loading (select_related/prefetch_related or selectinload).

How do I detect an N+1 query?

Look for a query inside a loop, and enable SQL logging or an APM trace - you'll see the same statement repeated many times per request with different ids.

Keep learning

Return the Correct HTTP Status CodeBackend projectRepair Broken API PaginationBackend projectAdd CORS Middleware to an Express APIBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →