ruby Rails:包含与:连接

kgqe7b3p  于 2022-11-29  发布在  Ruby
关注(0)|答案(8)|浏览(122)

这更像是一个“为什么事情会这样”的问题,而不是一个“我不知道怎么做”的问题...
因此,在提取您知道要使用的关联记录时,最好使用: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)

它 * 是 * 走捷径,因为它一次拉取所有注解,但它仍然不是一个连接(所有文档似乎都是这么说的)。

Post.all(:joins => :comments)

日志显示:

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

我错过了什么吗?我有一个应用程序,有六个关联,我在一个屏幕上显示所有这些关联的数据。似乎有一个连接的查询比6个单独的查询更好。我知道,从性能方面来说,连接并不总是比单独的查询更好(实际上,如果按所用时间计算,上面两个单独的查询似乎比连接更快),但是在我阅读了所有文档之后,我惊讶地发现:include并不像广告上说的那样工作。
也许Rails * 认识到了性能问题,除了在某些情况下,它不会加入?

6kkfgxo0

6kkfgxo01#

.joins将只连接表并返回所选字段.如果你调用连接查询结果上的关联,它将再次激发数据库查询
:includes将立即加载包含的关联并将其添加到内存中。:includes将加载所有包含的表属性。如果对包含查询结果调用关联,则不会触发任何查询

guz6ccqo

guz6ccqo2#

连接和包含语句之间的区别在于,使用包含语句会生成一个更大的SQL查询,该查询会将其他表中的所有属性加载到内存中。
例如,如果您有一个充满评论的表,并且您使用a:joins =〉users来获取所有用户信息以进行排序等,它将工作正常,并且比:include花费更少的时间,但是假设您想要显示评论以及用户名、电子邮件等。要使用:joins来获取信息,它将必须为它获取的每个用户进行单独的SQL查询,而如果您使用:include,则此信息已准备就绪。
很好的例子:
http://railscasts.com/episodes/181-include-vs-joins

chhkpiq4

chhkpiq43#

我最近阅读了更多关于Rails中:joins:includes之间的区别的文章。下面是我所理解的内容的解释(有例子:))
请考虑以下情况:

  • 一个用户有_多条注解,并且一条注解属于_一个用户。
  • 用户模型具有以下属性:姓名(字符串)、年龄(整数)。注解模型具有以下属性:内容、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(用户表)的所有记录。**因此,如果您这样做

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

它将获取comments.user_id为nil的记录,如图所示。
在内存中加载这两个表。

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

正如您所注意到的,comment_1.user.age只是从内存中加载结果,而不需要在后台触发数据库查询。

ippsafx7

ippsafx74#

除了性能方面的考虑之外,还有一个功能上的区别。当你加入评论时,你要求的是有评论的帖子--默认情况下是一个内部连接。当你包括评论时,你要求的是所有帖子--一个外部连接。

wnrlj8wa

wnrlj8wa5#

  • tl;dr*

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

联接-用于有条件地选择记录。
includes-对结果集得每个成员使用关联时.

  • 加长版 *

联接用于筛选来自数据库的结果集。您可以使用它对表执行集合操作。可以将其视为执行集合论的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

yptwkmov

yptwkmov6#

.joins作为数据库连接工作,它连接两个或多个表,并从后端(数据库)提取选定的数据。
.includes作为数据库的左连接工作。它加载左侧的所有记录,不具有右侧模型的相关性。它用于提前加载,因为它加载内存中的所有关联对象。如果我们对includes查询结果调用关联,则它不会对数据库触发查询,它只是从内存中返回数据,因为它已经在内存中加载了数据。

wlwcrazw

wlwcrazw7#

'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
但使用'includes'将立即加载包含的关联并将其添加到内存中(在第一次加载时加载所有关联),并且不会再次触发查询
当您获取包含的记录时,如@records= User.includes(:organizations).where(“organizations.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

records.map{|U形|u.organization.name}不会触发任何查询

guykilcj

guykilcj8#

:include功能似乎在Rails 2.1中发生了变化。Rails在所有情况下都用于执行连接,但出于性能原因,在某些情况下改为使用多个查询。Fabio秋田的This blog post提供了一些关于此变化的有用信息(请参见“优化的Eager Loading”一节)。

相关问题