因此,关于提取您知道的关联记录的福音您将要使用的是
: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 楼
似乎在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}
没有查询将触发
评论
如果您使用的是旧版的Rails,请通过标签或在问题正文中注明。否则,如果您正在使用Rails 4 NOW,则其中包括(供任何阅读此文件的人使用)现在也有:preload和:eager_loadblog.bigbinary.com/2013/07/01/…