这更像是一个“事情为什么会这样”的问题,而不是一个“我不知道怎么做”的问题。

拉关联记录的福音是使用:include因为你会得到一个连接,避免一大堆额外的查询:

Post.all(:include => :comments)

然而,当你查看日志时,没有连接发生:

Post Load (3.7ms)   SELECT * FROM "posts"
Comment Load (0.2ms)   SELECT "comments.*" FROM "comments" 
                       WHERE ("comments".post_id IN (1,2,3,4)) 
                       ORDER BY created_at asc) 

它采用了一种捷径,因为它一次拉出了所有的注释,但它仍然不是一个连接(所有文档似乎都是这么说的)。我能得到一个连接的唯一方法是使用:joins而不是:include:

Post.all(:joins => :comments)

日志显示:

Post Load (6.0ms)  SELECT "posts".* FROM "posts" 
                   INNER JOIN "comments" ON "posts".id = "comments".post_id

Am I missing something? I have an app with half a dozen associations and on one screen I display data from all of them. Seems like it would be better to have one join-ed query instead of 6 individuals. I know that performance-wise it's not always better to do a join rather than individual queries (in fact if you're going by time spent, it looks like the two individual queries above are faster than the join), but after all the docs I've been reading I'm surprised to see :include not working as advertised.

也许Rails意识到了性能问题,除非在某些情况下才会加入?


当前回答

.joins将只是连接表并返回所选字段。如果在连接查询结果上调用关联,它将再次触发数据库查询

:includes将立即加载包含的关联并将它们添加到内存中。:包含加载所有包含的表属性。如果您在include查询结果上调用关联,它将不会触发任何查询

其他回答

看来:include功能在Rails 2.1中有所改变。Rails过去在所有情况下都执行连接,但出于性能原因,在某些情况下更改为使用多个查询。Fabio Akita的这篇博客文章有一些关于这个变化的很好的信息(参见标题为“优化的快速加载”部分)。

我最近阅读了更多关于rails中:joins和:includes的区别。以下是我的理解(有例子:))

考虑一下这个场景:

一个User has_many comments和一个comment belongs_to一个User。 User模型有以下属性:名称(字符串),年龄(整数)。Comment模型有以下属性:Content, user_id。对于注释,user_id可以为空。

连接:

:joins执行两个表之间的内部连接。因此

Comment.joins(:user)

#=> <ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first   comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">, 
     #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,    
     #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">]>

将获取user_id(评论表)等于user的所有记录。Id (users表)。因此如果你这样做

Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>

您将得到一个空数组,如所示。

而且,连接不会在内存中加载已连接的表。因此如果你这样做

comment_1 = Comment.joins(:user).first

comment_1.user.age
#=> User Load (0.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1 [["id", 1]]
#=> 24

如您所见,comment_1.user。Age将在后台再次触发数据库查询以获得结果

包括:

include在两个表之间执行左外连接。因此

Comment.includes(:user)

#=><ActiveRecord::Relation [#<Comment id: 1, content: "Hi I am Aaditi.This is my first comment!", user_id: 1, created_at: "2014-11-12 18:29:24", updated_at: "2014-11-12 18:29:24">,
   #<Comment id: 2, content: "Hi I am Ankita.This is my first comment!", user_id: 2, created_at: "2014-11-12 18:29:29", updated_at: "2014-11-12 18:29:29">,
   #<Comment id: 3, content: "Hi I am John.This is my first comment!", user_id: 3, created_at: "2014-11-12 18:30:25", updated_at: "2014-11-12 18:30:25">,    
   #<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

将产生一个包含评论表中所有记录的连接表。因此如果你这样做

Comment.includes(:user).where("comment.user_id is null")
#=> #<ActiveRecord::Relation [#<Comment id: 4, content: "Hi This is an anonymous comment!", user_id: nil, created_at: "2014-11-12 18:31:02", updated_at: "2014-11-12 18:31:02">]>

它将获取注释所在的记录。如所示,User_id为nil。

而且包括在内存中加载两个表。因此如果你这样做

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

如你所见,comment_1.user。Age只需从内存中加载结果,而无需在后台触发数据库查询。

连接和include之间的区别在于,使用include语句生成一个更大的SQL查询,将来自其他表的所有属性加载到内存中。

例如,如果您有一个充满评论的表,并且您使用a:joins => users来拉入所有用户信息以进行排序,等等,它将工作得很好,并且比:include花费的时间要少,但是您想要显示评论以及用户名称、电子邮件等。要使用:joins获取信息,它必须为它获取的每个用户进行单独的SQL查询,而如果使用:include,则此信息已准备好使用。

很好的例子:

http://railscasts.com/episodes/181-include-vs-joins

.joins将只是连接表并返回所选字段。如果在连接查询结果上调用关联,它将再次触发数据库查询

:includes将立即加载包含的关联并将它们添加到内存中。:包含加载所有包含的表属性。如果您在include查询结果上调用关联,它将不会触发任何查询

除了性能方面的考虑外,还有功能上的差异。 当你连接评论时,你是在请求有评论的帖子——默认情况下是一个内部连接。 当你包含评论时,你是在请求所有的帖子——一个外部连接。