Skip to content

python.n_plus_one_queries

Performance High Performance Degradation

Detects database queries inside loops or ORM relationship access in iteration without eager loading.

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.

# ❌ Before (N+1 queries)
users = session.query(User).all()
for user in users:
print(user.orders) # Triggers a query per user

If 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 queries

Now it’s just 1-2 queries total.

  • Relationship access (.related_field) inside loops
  • Database queries (session.query, Model.objects) inside loops
  • Missing joinedload, selectinload, prefetch_related, select_related

Unfault can add appropriate eager loading to queries when the relationship and ORM are recognized.

# SQLAlchemy
from sqlalchemy.orm import joinedload, selectinload
# One-to-one or many-to-one
query.options(joinedload(User.profile))
# One-to-many or many-to-many
query.options(selectinload(User.orders))
# Django
# One-to-one or many-to-one
User.objects.select_related('profile')
# One-to-many or many-to-many
User.objects.prefetch_related('orders')