N+1 Query Problem
- In Turkish
- N+1 Sorgu Sorunu
In short
The N+1 query problem is a performance bug where code runs one query to load a list and then one extra query per item, instead of fetching it all at once.
What is the N+1 query problem?
The N+1 query problem happens when code fetches a list of N records with one query and then loops over them, running a separate query for each record's related data. That makes 1 + N queries in total. With 10 records it is barely noticeable, but with 1,000 records a single page load sends 1,001 queries to the database.
The problem is often hidden by an ORM's lazy loading: reading post.author inside a loop looks like a simple property access, but it silently runs a query each time. Every individual query is small and fast, yet the network round trips and per-query overhead add up to a slow page. The fix is to fetch related data in a fixed, small number of queries, using eager loading in the ORM, a single JOIN, or one batched query with WHERE id IN (...). GraphQL servers commonly solve it with a batching layer, often called a data loader, that collects IDs and loads them together.
It is like going to the grocery store once for each item on your shopping list instead of buying everything in one trip. You can spot the problem in database query logs, ORM debug output, or request traces that show the same query shape repeated dozens of times per request, and some teams add tests that fail if an endpoint exceeds a set number of queries.
The N+1 problem is different from a slow query. A slow query is one expensive statement that an index or rewrite can often fix, while N+1 is many cheap statements that each look fine on their own, so adding indexes does not help. Eager loading everything is not always the answer either, because loading related data a page never uses wastes memory and time.
At a glance
Key takeaways
- N+1 means one query for a list plus one query per item in that list.
- ORM lazy loading is the most common hidden cause.
- Each query is fast, but the round trips add up as the list grows.
- Fix it with eager loading, a
JOIN, or a batchedINquery. - Indexes don't fix N+1, because the problem is the number of queries.
Example
// N+1: 1 query for the posts, then 1 query per post for its author
const posts = await db.query("SELECT * FROM posts LIMIT 50");
for (const post of posts) {
const rows = await db.query("SELECT * FROM users WHERE id = ?", [post.author_id]);
post.author = rows[0];
}
// Fix: load all the authors in one extra query (2 queries in total)
const ids = posts.map((p) => p.author_id);
const authors = await db.query("SELECT * FROM users WHERE id IN (?)", [ids]);
const byId = new Map(authors.map((a) => [a.id, a]));
for (const post of posts) post.author = byId.get(post.author_id);Readers ask
How do I detect N+1 queries?
Turn on query logging in your ORM or database and look for the same query repeated with different IDs during one request. Tracing and performance monitoring tools, and some ORM plugins, can flag the pattern automatically.
Does using an ORM cause the N+1 problem?
The ORM doesn't cause it by itself, but lazy loading makes it very easy to write by accident. Most ORMs provide eager loading options, such as include or prefetch, to load related records up front.
Is a JOIN always better than N+1 queries?
Usually, but not always. A join can duplicate parent data across many rows, so for large one-to-many relationships two queries, one for parents and one batched query for children, are often cleaner and just as fast.
See also
- ORMDatabases, p. 33An ORM is a library that maps database tables to objects in your programming language, letting you read and write data with code instead of raw SQL.
- SQL JOINDatabases, p. 41A SQL JOIN is a query operation that combines rows from two or more tables into one result, matching them on related columns such as a foreign key.
- Database IndexDatabases, p. 7A database index is a data structure that helps a database find rows quickly without scanning a whole table, much like the index at the back of a book.
- LatencyNetworking, p. 14Latency is the delay between sending a request and the start of a response, usually measured in milliseconds, and it shapes how responsive an app feels.
- GraphQLBackend & APIs, p. 19GraphQL is a query language and runtime for APIs that lets clients request exactly the data they need, often from a single endpoint in a single request.
- Connection PoolDatabases, p. 3A connection pool is a cache of open database connections that an application reuses across requests, avoiding the cost of opening a new connection every time.
Spotted a mistake or something missing on this page?Suggest an edit