0
0
RailsHow-ToBeginner · 4 min read

N+1 Query Problem in Rails: How to Detect and Fix It

The N+1 query problem in Rails happens when your app runs one query to get records, then runs one query per record to get associated data, causing many database calls. You fix it by using includes to eager load associations, which loads all needed data in fewer queries.
📐

Syntax

Use includes to eager load associations and avoid the N+1 problem. The syntax is:

  • Model.includes(:association) loads the main model and its associated records in fewer queries.
  • You can chain it with where, order, and other ActiveRecord methods.
ruby
posts = Post.includes(:comments).where(published: true)
posts.each do |post|
  puts post.title
  post.comments.each do |comment|
    puts "- #{comment.body}"
  end
end
💻

Example

This example shows the N+1 problem and how to fix it with includes. Without includes, Rails runs one query for posts and one query per post to get comments. With includes, Rails runs two queries total.

ruby
# Without includes (N+1 problem)
posts = Post.all
posts.each do |post|
  puts post.title
  post.comments.each do |comment|
    puts "- #{comment.body}"
  end
end

# With includes (fixes N+1)
posts = Post.includes(:comments).all
posts.each do |post|
  puts post.title
  post.comments.each do |comment|
    puts "- #{comment.body}"
  end
end
Output
Post 1 - Comment A - Comment B Post 2 - Comment C Post 3 - Comment D - Comment E - Comment F
⚠️

Common Pitfalls

Common mistakes include:

  • Not using includes when accessing associations in loops, causing many queries.
  • Using joins instead of includes when you want to eager load, which can change results.
  • Forgetting to preload nested associations when needed.

Always check your logs or use tools like bullet gem to detect N+1 queries.

ruby
# Wrong: causes N+1 queries
posts = Post.all
posts.each do |post|
  puts post.title
  post.comments.each do |comment|
    puts comment.body
  end
end

# Right: eager loads comments
posts = Post.includes(:comments).all
posts.each do |post|
  puts post.title
  post.comments.each do |comment|
    puts comment.body
  end
end
📊

Quick Reference

ConceptDescriptionExample
N+1 Query ProblemMultiple queries run: 1 for main records + 1 per associated recordPost.all + post.comments in loop
Eager LoadingLoad associations in fewer queries to improve speedPost.includes(:comments).all
Detecting N+1Use Rails logs or bullet gem to find extra queriesCheck logs for repeated queries
AvoidUsing joins when you want to preload dataUse includes, not joins, for eager loading

Key Takeaways

The N+1 query problem causes many database calls and slows your Rails app.
Use includes to eager load associations and reduce queries.
Check your logs or use tools like bullet gem to find N+1 issues.
Avoid accessing associations inside loops without eager loading.
Eager loading improves performance by loading related data in fewer queries.