Rails 4 Eager load limit subquery

一笑奈何 提交于 2019-12-22 11:25:52

问题


Is there a way to avoid the n+1 problem when eager loading and also applying a limit to the subquery? I want to avoid lots of sql queries like this:

Category.all.each do |category|
  category.posts.limit(10)
end

But I also want to only get 10 posts per category, so the standard eager loading, which gets all the posts, does not suffice:

Category.includes(:posts).all

What is the best way to solve this problem? Is N+1 the only way to limit the amount of posts per category?


回答1:


From the Rails docs

If you eager load an association with a specified :limit option, it will be ignored, returning all the associated objects

So given the following model definition

class Category < ActiveRecord::Base
  has_many :posts
  has_many :included_posts, -> { limit 10 }, class_name: "Post"
end

Calling Category.find(1).included_posts would work as expected and apply the limit of 10 in the query. However, if you try to do Category.includes(:included_posts).all the limit option will be ignored. You can see why this is the case if you look at the SQL generated by an eager load

Category.includes(:posts).all

Category Load (0.2ms)  SELECT "categories".* FROM "categories"
Post Load (0.4ms)  SELECT "posts".* FROM "posts" WHERE "posts"."category_id" IN (1, 2, 3)

If you added the LIMIT clause to the posts query, it would return a total of 10 posts and not 10 posts per category as you might expect.

Getting back to your problem, I would eager load all posts and then limit the loaded collection using first(10)

categories = Category.includes(:posts).all
categories.first.posts.first(10)

Although you're loading more models into memory, this is bound to be more performant since you're only making 2 calls against the database vs. n+1. Cheers.



来源:https://stackoverflow.com/questions/29804377/rails-4-eager-load-limit-subquery

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!