这更多是“为什么事情会这样工作”的问题,而不是“我不知道该怎么办”的问题...

因此,关于提取您知道的关联记录的福音您将要使用的是: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可能意识到性能问题并且除了某些情况下没有加入?

评论

如果您使用的是旧版的Rails,请通过标签或在问题正文中注明。否则,如果您正在使用Rails 4 NOW,则其中包括(供任何阅读此文件的人使用)

现在也有:preload和:eager_loadblog.bigbinary.com/2013/07/01/…

#1 楼

似乎在Rails 2.1中更改了:include功能。 Rails曾经在所有情况下都执行过联接,但是出于性能原因,在某些情况下将其更改为使用多个查询。 Fabio Akita撰写的此博客文章提供了有关此更改的一些很好的信息(请参见标题为“优化的紧急加载”的部分)。

评论


参见:samsaffron.com/archive/2008/03/15/…

– Sam Saffron
09年7月31日在13:16

这非常有帮助,谢谢。我希望有一种方法可以强迫Rails进行连接,即使没有“需要”的地方。在某些情况下,您知道联接将更有效,并且不会产生重复的风险。

–乔纳森·斯沃兹(Jonathan Swartz)
13年11月21日,0:28

另请参阅:blog.bigbinary.com/2013/07/01/…

–内森·朗(Nathan Long)
2014年8月6日15:34

@JonathanSwartz看起来新版本的Rails使用eagerload支持此功能。感谢您的链接NathanLong

–rubyprince
17年5月12日在7:11

#2 楼

.joins只会联接表并带来所选字段作为回报。如果在联接查询结果上调用关联,它将再次触发数据库查询。

:includes将渴望加载所包含的关联并将其添加到内存中。 :includes加载所有包含的表属性。如果您对包含查询结果调用关联,则不会触发任何查询

#3 楼

join和include之间的区别在于,使用include语句会生成一个更大的SQL查询,它将其他表中的所有属性加载到内存中。例如,如果您有一个充满了以下内容的表:注释,并且您使用:joins => users来提取所有用户信息以进行排序,等等,它可以正常工作,并且所需的时间比:include少,但是您要显示注释以及用户名,电子邮件,等等。要使用:joins获取信息,它将必须为其获取的每个用户分别进行SQL查询,而如果使用了:include,则此信息可供使用。

很好的例子:

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

#4 楼

我最近在阅读有关:joins:includes在rails之间的区别的更多信息。以下是对我所理解的解释(带有示例:)。请考虑以下情形:


一个用户has_many注释和一个注释属一个用户。
用户模型具有以下属性:名称(字符串),年龄(整数)。 Comment模型具有以下属性:Content,user_id。对于注释,user_id可以为null。

联接:

: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
#=>←[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">]>


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

而且包括将两个表都加载到记忆。因此,如果您这样做

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24


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

评论


@HunterStevens:是的

–阿迪蒂(Aaditi Jain)
15/12/22在12:41

#5 楼

除了性能方面的考虑之外,功能上也有区别。
加入评论时,您要的是带有评论的帖子-默认情况下是内部联接。
包含评论时,您要的是所有帖子-外部联接。

#6 楼

tl; dr

我通过两种方式对它们进行对比:

joins-用于条件选择记录。

includes-在每一个上使用关联时结果集的成员。

较长的版本

联接用于过滤来自数据库的结果集。您可以使用它对表进行设置操作。将此视为执行集合论的where子句。

Post.joins(:comments)



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

相同除非有多条评论,否则您将获得包含联接的重复帖子。但是每个帖子都是带有评论的帖子。您可以使用以下独特的方法更正此问题:

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


契约中,includes方法将简单地确保在引用该关系时没有其他数据库查询(因此,不会进行n + 1个查询)

道德是,要进行条件集操作时请使用joins,而在打算使用a时要使用includes集合的每个成员上的关系。

评论


每次都会让我感到与众不同。谢谢!

–本·赫尔
18/12/14在1:41

#7 楼

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

.includes作为数据库的左联接。它加载了左侧的所有记录,与右侧模型没有任何关系。它用于渴望加载,因为它会将所有关联的对象加载到内存中。如果我们在包含查询结果上调用关联,则它不会在数据库上触发查询,它只是从内存中返回数据,因为它已经在内存中加载了数据。

#8 楼

“联接”仅用于联接表,当您在联接上调用关联时,它将再次触发查询(这意味着将触发许多查询)。

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(:organisations).where(“ organisations.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.organisation.name}
没有查询将触发