[ GLOSSARY ]

What is an N+1 Query Problem?

QUICK ANSWER

What is an N+1 query?

An N+1 query is a performance anti-pattern where loading a list of N parent records triggers N additional queries — one per parent — to fetch a related record for each. Instead of 2 efficient queries (parents + batched children), you fire 1 + N queries, producing linear performance degradation as the list grows.

Updated · 2026-04-13

Why the name?

The "1" is the initial query that returns a list of N rows. The "N" is the N follow-up queries — one per row — to load related data. For 50 posts loaded with their author, that's 51 total queries instead of the 2 you actually need.

The classic Laravel example

N+1 — fires 1 + 50 = 51 queries

php
// 1 query: SELECT * FROM posts ORDER BY ... LIMIT 50
$posts = Post::latest()->take(50)->get();

foreach ($posts as $post) {
    // 50 queries: SELECT * FROM users WHERE id = ?
    echo $post->author->name;
}

Fixed — fires 2 queries

php
// 2 queries: 1 for posts, 1 batched for all authors
$posts = Post::with('author')->latest()->take(50)->get();

foreach ($posts as $post) {
    echo $post->author->name; // already in memory
}

Why it matters

Every query has network and protocol overhead — typically 1-5ms on a well-tuned system. The queries themselves might be fast, but 50 round-trips stack up: a page that should run in 50ms runs in 250-500ms. Scale that to 500 items and you're in seconds territory.

N+1 is the single most common cause of slow Laravel endpoints. Finding and fixing one often cuts a page's response time by 5-10x.

How to detect N+1 in production

Model::preventLazyLoading() catches them in development. In production you need an APM that groups repeated query fingerprints per request — the signal is "the same normalized SQL fired 47 times in one request."

NightOwl gives you the data to spot it without auto-flagging: the query watcher fingerprints every statement and exposes a call_count column on the Queries page. The request detail page lists every query the request fired, so a repeated pattern is obvious at a glance — but the diagnosis is yours, not an automated threshold.

RELATED

Frequently asked questions

Why is it called N+1?

The '1' is the initial query that returns a list of N parent records. The 'N' is the N additional queries — one per parent — to load a related record for each. Total: 1 + N. For a list of 50 items, that's 51 queries instead of the 2 you actually need.

Is an N+1 always a bug?

Practically yes, though the severity varies. In a 5-item list it's a small waste. In a 500-item list it's the difference between a 50ms page and a 5-second page. Database round-trips dominate query latency — each trip is cheap, 500 trips in a loop are not.

How much slower is an N+1 query?

Each database round-trip costs 1-5ms of pure network/protocol overhead on top of execution. A 100-item list with an N+1 adds 100-500ms of latency — often 10x slower than the fixed version, even if the queries themselves are fast.

Does eager loading always fix N+1 queries?

Yes, but choose the right variant. with() on the query, load() after the fact, loadMissing() for conditional loads, morphWith() for polymorphic relations. For column-selection use with('author:id,name'). For nested relations use dotted syntax: with('posts.comments').

PRICING

Flat pricing. No event caps. No per-seat fees.

14-day free trial, no credit card. Your PostgreSQL, your data.

HOBBY

$5 /month

1 app · 14 days lookback · all Laravel events

TEAM

$15 /month

Up to 3 connected apps · unlimited environments · all Laravel events

AGENCY

$69 /month

Unlimited apps · unlimited agent instances · same flat rate at any traffic