我提出了一个应该开箱即用的问题的例子,只要你有sqlite3可用:https://github.com/cairo140/rails-eager-loading-counts-demo
安装说明(主分支)
git clone git://github.com/cairo140/rails-eager-loading-counts-demo.git cd rails-eager-loading-counts-demo rails s
我在存储库中有更全面的写作,但我的一般问题是这样.
如何使Rails能够以最大限度地减少数据库查询的方式使用Rager的加载计数?
尽管在ActiveRelation中通过#includes(:associated)包含了该关联,但在使用#count关联时,n 1问题就会出现.解决方法是使用#length,但是只有当被调用的对象已经被加载时,这样才能很好地工作,更不用说我怀疑它与Rails内部已经做的事情重复了.此外,使用#length的问题是,当关联未加载开始并且计数是所有您需要的时候,它会导致不幸的过载.
从自述:
We can dodge this issue by running #length on the posts array (see appendix),which is already loaded,but it would be nice to have count readily available as well. Not only is it more consistent; it provides a path of access that doesn’t necessarily require posts to be loaded. For instance,if you have a partial that displays the count no matter what,but half the time,the partial is called with posts loaded and half the time without,you are faced with the following scenario:
- Using
#count
- n
COUNT
style queries when posts are already loaded- n
COUNT
style queries when posts are not already loaded- Using
#length
- Zero additional queries when posts are already loaded
- n
*
style queries when posts are not already loadedBetween these two choices,there is no dominant option. But it would be nice to revise #count to defer to #length or access the length that is some other way stored behind the scenes so that we can have the following scenario:
- Using revised
#count
- Zero additional queries when posts are already loaded
- n
COUNT
style queries when posts are not already loaded
那么这里有什么正确的方法呢?有没有我忽视的东西(很可能)?
解决方法
您可以非常聪明地使用原始模型与set_table_name结合使用子类:sql_view_name来保留对象上的所有原始方法,甚至可能还包含其中一些关联.
例如,我们要在你的例子中添加“Post.has_many:comments”,就像在@ Zubin的上面的答案一样;那么人们可能会做到:
class CreatePostsWithCommentsCountsView < ActiveRecord::Migration def self.up #Create sql View called posts_with_comments_counts which maps over # select posts.*,count(comments.id) as comments_count from posts # left outer join comments on comments.post_id = posts.id # group by posts.id # (As zubin pointed out above.) #*Except* this is in sql so perhaps we'll be able to do further # reducing queries against it *as though it were any other table.* end end class PostWithCommentsCount < Post #Here there be cleverness. #The class definition sets up PWCC # with all the regular methods of # Post (pointing to the posts table # due to Rails' STI facility.) set_table_name :posts_with_comment_counts #But then we point it to the # sql view instead. #If you don't really care about # the methods of Post being in PWCC # then you could just make it a # normal subclass of AR::Base. end PostWithCommentsCount.all(:include => :user) #ObvIoUsly,this sort of "upward # looking" include is best used in big lists like "latest posts" rather than # "These posts for this user." But hopefully it illustrates the improved # activerecordiness of this style of solution. PostWithCommentsCount.all(:include => :comments) #And I'm pretty sure you # should be able to do this without issue as well. And it _should_ only be # the two queries.