在我的代码中,我加入了2个模型(例如Post和Comment),然后我急切地加载第三个模型(例如作者)。当我打印每条记录时, rails使用热门加载的模型数据作为Post的第一个实例,但会为Post的每个其他实例触发其他数据库查询。这有点像n + 1问题。
我不明白为什么我只能使用热切加载的数据作为每篇文章的第一条记录而不是加入返回的其他记录?
我看过类似的问题,并不认为这些问题解决了同样的问题。其中包括:
我还查看了rails源代码,特别是ActiveRecord::Associations::Preloader,并尝试在本地稍微修改它,看看我是否可以缩小问题的位置或者是否发生在这里。我曾经想过,这个类可能正在通过id剥离非唯一记录实例(我认为它在第92行)但是当我改变它时我的问题没有得到解决。
我偶然发现这个似乎适用的whacky edge case,但在我的实际代码中,我使用find_by_sql
并且无法有效地实现这一点。
我创建了一个 gist with steps to reproduce the issue 我的体验和我得到的输出。任何帮助将不胜感激。
注意:此示例是我能想到的最简单的设置,它演示了我在实际代码中遇到的相同问题。在我的示例中我知道我可以急于加载评论然而,在我的实际代码中,有一个更复杂的连接,并且需要连接。我无法从此示例中急切加载Comment模型。
答案 0 :(得分:0)
对于那些将来发现这一点的人来说,这就是我发现的以及我实施的内容。
对于我的要点中概述的简单示例,我发现我可以获得所需的结果来改变以下内容。
要点:
# Triggers n+1 for author after first puts of an author.
posts = Post.joins(:comments).select("posts.*, comments.body").includes(:author)
posts.each do |x|
puts "#{x.title} #{x.author.name} #{x.body}"
end
更改为:
# Works as expected.
posts = Post.joins(:comments, :author).select("posts.*, comments.body").includes(:author)
posts.each do |x|
puts "#{x.title} #{x.author.name} #{x.body}"
end
通过加入author
,我得到了我想要的结果,而无需其他查询。
虽然以上解决了我用来演示我的问题的简单例子,但在我更复杂的现实问题中(如我的问题中所述),我无法实现这个额外的连接来解决问题。
相反,我实现了这个伟大的Thoughbot post和github上的rails手册(Query Object Pattern和Decorator Pattern)中概述的查询对象模式和装饰器模式。
这些模式让我可以获得所需的特定关联的所有好处,但是对于我更复杂的查询。