Rails:include vs.:join

送分小仙女□ 提交于 2020-03-09 10:53:44

这更像是“为什么会这样做”这个问题,而不是“我不知道该怎么做”这个问题......

所以关于拉你知道你将要使用的相关记录的福音是使用: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

我错过了什么吗? 我有一个有六个关联的应用程序,在一个屏幕上我显示所有这些数据。 似乎最好有一个加入查询而不是6个人。 我知道在性能方面,进行连接而不是单个查询并不总是更好(事实上,如果你花费时间,看起来上面的两个单独的查询比连接更快),但是在所有文档之后我一直在读,我很惊讶地看到:include不按宣传方式工作。

也许Rails 认识的性能问题,并除非在某些情况下,不加入呢?


#1楼

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

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


#2楼

似乎使用Rails 2.1改变了:include功能。 Rails用于在所有情况下进行连接,但出于性能原因,在某些情况下将其更改为使用多个查询。 Fabio Akita的这篇博客文章提供了有关变化的一些很好的信息(参见标题为“Optimized Eager Loading”的部分)。


#3楼

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

例如,如果你有一个充满注释的表,并且你使用:joins => users来提取所有用户信息以进行排序等,它将正常工作并且花费的时间少于:include,但是你想要显示注释以及用户名,电子邮件等。要使用以下方式获取信息:连接,它必须为其提取的每个用户单独进行SQL查询,而如果您使用:包含此信息已准备就绪,可以使用。

好例子:

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


#4楼

.joins用作数据库连接,它连接两个或多个表并从后端(数据库)获取所选数据。

.includes作为数据库的左连接工作。 它加载了左侧的所有记录,没有右侧模型的相关性。 它用于急切加载,因为它加载内存中的所有关联对象。 如果我们在include查询结果上调用关联,那么它不会在数据库上触发查询,它只是从内存中返回数据,因为它已经在内存中加载了数据。


#5楼

我最近在阅读更多关于:joins:includes在rails中的区别。 这是我理解的解释(用例子:))

考虑这种情况:

  • 用户has_many评论和评论belongs_to用户。

  • User模型具有以下属性:Name(字符串),Age(整数)。 Comment模型具有以下属性:Content,user_id。 对于注释,user_id可以为null。

连接:

join在两个表之间执行内部联接。 从而

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
#=>←[1m←[36mUser Load (0.0ms)←[0m  ←[1mSELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT 1←[0m  [["id", 1]]
#=> 24

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

包括:

:includes在两个表之间执行左外连接 。 从而

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">]>

它将获取comments.user_id为零的记录,如图所示。

此外,还包括加载内存中的表。 因此,如果你这样做

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

您可以注意到,comment_1.user.age只是从内存加载结果而不在后台触发数据库查询。

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