include vs.:joins

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

因此,关于提取关联记录的福音,你知道你将使用: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 认识到性能问题,除非在某些情况下不加入?

170094 次浏览

:include功能似乎在Rails 2.1中被改变了。Rails过去在所有情况下都执行连接,但出于性能原因,在某些情况下更改为使用多个查询。这篇博文由Fabio秋田有一些很好的信息的变化(见节题为“优化急切加载”)。

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

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

很好的例子:

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

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

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

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

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

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

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

考虑一下这个场景:

  • 一个User has_many comments和一个comment belongs_to A 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将再次在后台触发数据库查询以获取结果

包括:

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

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

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

comment_1 = Comment.includes(:user).first


comment_1.user.age
#=> 24

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

'joins'只是用来连接表当你在连接上调用关联时它会再次触发查询(这意味着会触发很多查询)

lets suppose you have tow model, User and Organisation
User has_many organisations
suppose you have 10 organisation for a user
@records= User.joins(:organisations).where("organisations.user_id = 1")
QUERY will be
select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1


it will return all records of organisation related to user
and @records.map{|u|u.organisation.name}
it run QUERY like
select * from organisations where organisations.id = x then time(hwo many organisation you have)

在本例中,SQL的总数为11

< p >但 'includes'将立即加载所包含的关联并将它们添加到内存中(在第一次加载时加载所有关联),而不会再次触发查询

当你得到记录包含像 @records = User.includes(组织)。(“组织。User_id = 1") 那么查询将是

select * from users INNER JOIN organisations ON organisations.user_id = users.id where organisations.user_id = 1
and




select * from organisations where organisations.id IN(IDS of organisation(1, to 10)) if 10 organisation
and when you run this
u < p > @records.map {| | u.organisation.name} 没有查询将触发

博士tl;

我从两个方面对它们进行对比:

连接用于条件选择记录。

包括 -当对结果集的每个成员使用关联时。

完整版

join用于过滤来自数据库的结果集。你可以用它在表上做set操作。把它看作是执行集合理论的where子句。

Post.joins(:comments)

Post.where('id in (select post_id from comments)')

除了如果有一个以上的评论,你会得到重复的帖子与连接。但是每个帖子都有评论。你可以用distinct来纠正这个错误:

Post.joins(:comments).count
=> 10
Post.joins(:comments).distinct.count
=> 2

在契约中,includes方法将简单地确保在引用关系时没有额外的数据库查询(这样我们就不会进行n + 1次查询)

Post.includes(:comments).count
=> 4 # includes posts without comments so the count might be higher.

寓意是,当你想要进行条件集操作时使用joins,当你要对集合的每个成员使用关系时使用includes