What is an N+1 query?

An N+1 query problem happens when code runs one query to load a list of N records, then one more query per record for related data, so a page with 50 orders makes 51 database round trips instead of 2. It usually comes from ORM lazy loading and is fixed with eager loading or a join.

Updated · by PuzariCloud · also called N+1 query problem, N+1 selects

Why an N+1 query matters

Each query is fast, so nothing looks wrong in a slow-query log. The cost is in the count: round trips add up, connections are held longer, and the page gets slower as the list grows. A page that is fine with 10 test records can be slow with 1,000 real ones.

The same pattern hides in code that calls another service in a loop, where each iteration costs a network round trip instead of a query.

How to measure an N+1 query

queries ≈ 1 + N   (the list query plus one per item)
time    ≈ (1 + N) × round-trip time
fixed:  2 queries (the list plus one IN query) or 1 join

Worked example

A page lists 50 orders and shows each order's customer. Each query takes about 1.5 ms including the network round trip.

  1. Lazy loading: 1 + 50 = 51 queries, about 51 × 1.5 = 76.5 ms in the database alone.
  2. Eager loading (WHERE customer_id IN (…)): 2 queries, about 3 ms.
  3. With 500 orders the lazy version needs 501 queries; the eager version still needs 2.

Result: The fix changes neither the data nor the page, only the query shape, and the cost stops growing with the list.

Common mistakes

  • Testing with tiny data. With 5 rows an N+1 is invisible. Test with realistic data volumes.
  • Fixing it with a cache. Caching each item hides the pattern but keeps the round trips on a cold cache. Fix the query shape.
  • Over-eager loading. Loading every relation everywhere fetches data you do not use. Load what the view needs.
  • Only looking at slow queries. N+1 queries are fast one by one. Look at the number of queries per request.

How Vigil handles an N+1 query

  • Vigil's APM flags an N+1 when a single trace contains 8 or more queries of the same shape, and marks those rows on the trace timeline.
  • Slow queries are ranked by total time (calls × duration), which surfaces fast but frequent queries that a per-query threshold misses. The Python SDK instruments SQLAlchemy and the Node SDK instruments pg and mysql2.
  • The Django and Rails guides cover what else to watch in those frameworks.

N+1 query: frequently asked questions

How do I fix an N+1 query?

Load the related records in one go: select_related or prefetch_related in Django, includes in Rails, joinedload or selectinload in SQLAlchemy, with() in Laravel Eloquent. Then check the query count again.

Is N+1 always bad?

Not for tiny, bounded lists where the extra queries are cheap. It becomes a problem when N grows with your data or traffic.

Why is it called N+1?

Because the code runs 1 query for the list and then N more, one for each item in it.

Vigil by PuzariCloud

Measure it instead of estimating it

Uptime and API checks, heartbeats, status pages, on-call, traces, errors, logs and real user monitoring in one place, with every feature on every plan, including Free.