python.n_plus_one_queries
Performance
High
Performance Degradation
Detects database queries inside loops or ORM relationship access in iteration without eager loading.
Why It Matters
Section titled “Why It Matters”N+1 queries are one of the most common performance killers:
- Linear scaling — 100 items = 101 queries, 10,000 items = 10,001 queries
- Latency explosion — Network round-trips add up fast
- Database load — Hammers your database with many small queries
- Hidden in code — ORM makes it easy to trigger accidentally
What takes 2 queries with proper loading takes thousands without it.
Example
Section titled “Example”# ❌ Before (N+1 queries)users = session.query(User).all()for user in users: print(user.orders) # Triggers a query per userIf you have 100 users, this executes 101 queries (1 for users + 100 for orders).
# ✅ After (eager loading)from sqlalchemy.orm import joinedload
users = session.query(User).options(joinedload(User.orders)).all()for user in users: print(user.orders) # No additional queriesNow it’s just 1-2 queries total.
What Unfault Detects
Section titled “What Unfault Detects”- Relationship access (
.related_field) inside loops - Database queries (
session.query,Model.objects) inside loops - Missing
joinedload,selectinload,prefetch_related,select_related
Auto-Fix
Section titled “Auto-Fix”Unfault can add appropriate eager loading to queries when the relationship and ORM are recognized.
ORM-Specific Patterns
Section titled “ORM-Specific Patterns”# SQLAlchemyfrom sqlalchemy.orm import joinedload, selectinload
# One-to-one or many-to-onequery.options(joinedload(User.profile))
# One-to-many or many-to-manyquery.options(selectinload(User.orders))
# Django# One-to-one or many-to-oneUser.objects.select_related('profile')
# One-to-many or many-to-manyUser.objects.prefetch_related('orders')