On this page
  1. What is the N+1 query problem?
  2. A concrete example
  3. Why ORMs cause it
  4. Where AI coding assistants get this wrong
  5. Checklist
  6. FAQ
    1. Is N+1 always caused by an ORM?
    2. What is eager vs lazy loading?
    3. How do I detect N+1 queries?
  7. Related topics
  8. Sources
concept

What Is the N+1 Query Problem?

The N+1 problem fires one query per row instead of one query total, and AI-generated ORM code causes it constantly. Learn it with a code example.

Quick answer

  • The N+1 problem is when code runs one query to fetch a list, then one more query per item — N items plus 1 initial query.
  • It comes from ORMs’ lazy loading: accessing a related object triggers a query behind your back.
  • The fix is eager loading, which fetches the related data in one or two queries instead of N+1.

What is the N+1 query problem?

The N+1 problem is an accidental multiplication of database queries: your code runs one query to load a list of N items, and then, for each item, runs another query to load its related data. Total: N+1 queries instead of 2. It’s invisible in development with a few rows and catastrophic with thousands, and it’s one of the most common performance bugs in object-relational mapping (ORM) code.

A concrete example

# N+1: one query for posts, then one query per post's author
posts = session.query(Post).all()
for post in posts:
    print(post.author.name)   # triggers a query each iteration

With 1,000 posts, that’s 1,001 queries. The fix fetches authors up front:

# Eager load: one query for posts, one for all their authors
posts = session.query(Post).options(joinedload(Post.author)).all()

Why ORMs cause it

ORMs use lazy loading by default: a related object isn’t loaded until you access it, so accessing post.author in a loop silently issues a query. The behavior is convenient for single objects and a trap for lists. This is exactly the kind of code AI assistants generate — it’s correct, it works in the demo, and it performs terribly at scale.

Where this bites vibecoders

The N+1 is the signature AI-generated performance bug: an assistant building a “list posts with their authors” endpoint writes the lazy-loading loop by default because that’s the simplest correct code. The bug only shows up with real data. The fix is a habit: whenever code loops over query results and touches a related object, ask whether it’s eager-loaded — or watch the query count in your logs.

Where AI coding assistants get this wrong

  • Generating lazy-loading loops for list endpoints.
  • Failing to suggest eager loading (joinedload, includes, preload) for related data.
  • Fixing one N+1 while introducing another in a sibling endpoint.
  • Never mentioning query-count monitoring as a safeguard.

Checklist

  • Watch query counts in development logs, not just response times.
  • Use eager loading for any loop that touches related objects.
  • Prefer joins or includes over per-item lookups.
  • Test with realistic row counts, not a handful of fixtures.
  • Add a query-count guard to catch regressions.

FAQ

Is N+1 always caused by an ORM?

No — you can write the same bug in plain SQL by querying in a loop. But ORMs make it easy to trigger accidentally via lazy loading, which is why the term is so associated with them.

What is eager vs lazy loading?

Lazy loading fetches related data only when accessed (one query per access, hence N+1). Eager loading fetches related data up front, alongside the main query, in one or two queries. Eager loading is the fix for list iteration.

How do I detect N+1 queries?

Query logging is the direct signal: you’ll see a flood of near-identical queries. Many frameworks also have N+1 detectors that warn during development. The symptom is endpoints that get slow as data grows while the code looks fine.

Sources

Share: