← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

N+1 Query Problem

database PHP 5.1+ Intermediate

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;
}

Added 31 Mar 2026
Edited 5 Apr 2026
Views 16
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Perplexity 6 Google 2 ChatGPT 1 Unknown AI 1 Ahrefs 1
crawler 10 crawler_json 1
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

✓ schema.org compliant