Share This Article
If you want to solve N+1 query issues in Ruby, you need to use the ActiveRecord#eager_load or ActiveRecord#includes methods.
# without eager loading
posts = Post.all
posts.each do |post|
puts post.user.name
end
# with eager loading
posts = Post.includes(:user)
posts.each do |post|
puts post.user.name
end
The ActiveRecord#includes method will eagerly load the associations (in this case, the user association). So when you iterate over the posts collection, the user object will already be loaded in memory, and you won’t have to do an extra query for each post.
The ActiveRecord#eager_load method is similar to the #includes method, but it will also load the associations for the posts already in memory.
# with eager loading
posts = Post.all
posts.each do |post|
puts post.user.name
end
puts posts.first.user.name
With the first code snippet, you will see that there’s an extra query when you call the puts posts.first.user.name
line because the user object is not loaded in memory yet.
While with the second code snippet, the user object is already loaded in memory, so there’s no extra query.
You can also use the #eager_load method with the #includes method.
# with eager loading
posts = Post.includes(:user).eager_load(:comments)
posts.each do |post|
puts post.user.name
end
puts posts.first.user.name
The code above will eager load the user and comment associations.
Why Use Eager Loading?
Eager loading is proper when you have a lot of data, and you want to avoid doing too many queries.
It’s also helpful when using paginated data, so you don’t have to do an extra query for each page.
When Not to Use Eager Loading
There are some cases when you should not use eager loading.
For example, when you are only fetching a few records. In this case, it’s better to use the #find_by or #find_by! methods.
# with eager loading
posts = Post.includes(:user).find_by(id: 1)
# without eager loading
posts = Post.find_by(id: 1)
Another case where eager loading is not a good idea is when you return a significant json response because loading all the associations in memory can lead to a memory leak.
In this case, you can use the #as_json method with the #only or #except methods.
# with eager loading
posts = Post.includes(:user).as_json(only: [:id, :user_id])
# without eager loading
posts = Post.as_json(except: [:user])
When fetching a significant json response, you should only include the necessary attributes.
This way, you can avoid loading too much data in memory.
Summary
In this article, you’ve learned how to use the ActiveRecord#eager_load, and #includes methods to solve the N+1 queries problem.
You’ve also learned when eager loading is a good idea and when it’s not.
It would help if you used eager loading when you have a lot of data, and you want to avoid doing too many queries.
It would help if you did not use eager loading when you are only fetching a few records or returning a significant json response.