N+1 Query Problem
Also Known As
N+1 query problem
N+1 queries
lazy load N+1
TL;DR
Executing one query to get N records, then N more queries to fetch related data — one per record.
Explanation
The N+1 problem occurs when code fetches a list of entities and then executes an additional query for each one — typically to load a related record. For 100 rows this means 101 queries; for 1000 rows, 1001. Database round-trip overhead makes this disproportionately slow compared to a single JOIN or a single IN() query. The fix is to pre-load all related data in one query and look it up from a keyed array in memory.
Diagram
sequenceDiagram
participant APP as Application
participant DB as Database
APP->>DB: SELECT * FROM posts - 1 query
DB-->>APP: 100 posts returned
loop For each of 100 posts
APP->>DB: SELECT * FROM users WHERE id = ?
DB-->>APP: 1 user
end
Note over APP,DB: 1 + 100 = 101 queries total
Note over APP,DB: Fix: eager load with JOIN<br/>SELECT * FROM users WHERE id IN (1,2,3...)
Common Misconception
✗ The N+1 problem only affects ORMs. Any code that queries inside a loop — fetching a user's avatar for each post in a list — produces N+1 queries. ORMs make it easier to introduce accidentally; the underlying problem is a missing JOIN or eager load regardless of abstraction.
Why It Matters
N+1 queries are the most common reason a page that looks fine locally becomes unusable under real data — 100 rows means 101 queries, 1000 rows means 1001. Eager loading eliminates this with a single extra JOIN or IN query.
Common Mistakes
- Accessing a relationship inside a loop without eager loading — ORM lazy-loads on every iteration.
- Adding with() to the outer query but forgetting nested relationships (with('orders.items') instead of with('orders')).
- Fixing N+1 in controllers but leaving it in API resources or view composers that loop over collections.
- Not installing a query debugger (Laravel Debugbar, DBAL logger) — N+1 is invisible without query counting.
Code Examples
✗ Vulnerable
// N+1: 1 query for posts + N queries for author names
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // triggers a new query per post
}
✓ Fixed
$posts = Post::with('author')->get(); // 2 queries total
foreach ($posts as $post) {
echo $post->author->name;
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
13 Mar 2026
Edited
22 Mar 2026
Views
36
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Perplexity 9
Amazonbot 7
Google 4
Ahrefs 3
Unknown AI 2
SEMrush 1
Also referenced
How they use it
crawler 26
Related categories
⚡
DEV INTEL
Tools & Severity
🟠 High
⚙ Fix effort: Low
⚡ Quick Fix
Use eager loading (with() in Laravel, addSelect/join in Doctrine) to fetch related data in one query instead of N+1
📦 Applies To
PHP 5.0+
web
cli
queue-worker
laravel
doctrine
eloquent
🔗 Prerequisites
🔍 Detection Hints
Loop over collection calling ->relation or ->find() per iteration; Laravel Debugbar shows 50+ identical queries
Auto-detectable:
✓ Yes
laravel-debugbar
clockwork
phpstan
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Medium
✗ Manual fix
Fix: Medium
Context: File
Tests: Update