N+1 Query Problem
Also Known As
N+1 problem
lazy loading N+1
query loop anti-pattern
TL;DR
A performance anti-pattern where fetching N records triggers N additional queries — one per record — instead of a single JOIN.
Explanation
Classic example: fetch 100 users (1 query), then loop and fetch each user's orders separately (100 queries) = 101 queries total. The fix is eager loading — a single JOIN or a second query with WHERE user_id IN (...). ORMs like Eloquent and Doctrine make N+1 easy to introduce through lazy-loading relationships. SQL query logging or a profiler (Blackfire, Laravel Debugbar) reveals N+1 patterns.
Diagram
sequenceDiagram
participant PHP
participant MySQL
PHP->>MySQL: SELECT * FROM users (1 query)
loop For each of N users
PHP->>MySQL: SELECT * FROM orders WHERE user_id=?
end
Note over PHP,MySQL: N+1 queries total<br/>Fix: JOIN or WHERE IN
Common Misconception
✗ N+1 only matters with large datasets. Even with 20 records, N+1 adds 20 round-trips to the database — at 1ms per round-trip that's 20ms of pure overhead before any processing.
Why It Matters
N+1 queries turn a page that should take 5ms into one that takes 500ms under real data volumes. It's one of the most common performance bugs in web applications.
Common Mistakes
- Calling a database function inside a foreach loop over query results.
- Using ORM lazy-loading relationships without checking the query count in development.
- Fixing N+1 with N separate queries instead of a single IN clause.
Avoid When
- Never call a database query inside a foreach loop over query results without batching.
When To Use
- Use JOIN or WHERE id IN (...) to batch-load related records in one query.
- Use eager loading in ORMs (with(), load()) to prevent automatic lazy-loading N+1.
Code Examples
✗ Vulnerable
// N+1: 1 query for users + N queries for orders
$users = $pdo->query('SELECT * FROM users')->fetchAll();
foreach ($users as $user) {
$stmt = $pdo->prepare('SELECT * FROM orders WHERE user_id = ?');
$stmt->execute([$user['id']]);
$user['orders'] = $stmt->fetchAll(); // N separate queries!
}
✓ Fixed
// Fix: one query with JOIN or IN clause
$userIds = array_column($users, 'id');
$placeholders = implode(',', array_fill(0, count($userIds), '?'));
$stmt = $pdo->prepare("SELECT * FROM orders WHERE user_id IN ($placeholders)");
$stmt->execute($userIds);
$orders = $stmt->fetchAll();
// Group by user_id in PHP
$ordersByUser = [];
foreach ($orders as $order) {
$ordersByUser[$order['user_id']][] = $order;
}
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
31 Mar 2026
Edited
5 Apr 2026
Views
16
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Perplexity 6
Google 2
ChatGPT 1
Unknown AI 1
Ahrefs 1
Also referenced
How they use it
crawler 10
crawler_json 1
Related categories
⚡
DEV INTEL
Tools & Severity
⚙ Fix effort: Medium
⚡ Quick Fix
Use a JOIN or WHERE id IN (...) to batch-load related records in one query instead of one query per record
📦 Applies To
PHP 5.1+
web
cli
🔗 Prerequisites
🔍 Detection Hints
PDO prepare/execute inside foreach over query results
Auto-detectable:
✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: High
False Positives: Low
✗ Manual fix
Fix: Medium
Context: Function
Tests: Update