Rails:包括vs.:连接
这更多的是“为什么这样做”这个问题,而不是“我不知道该怎么做”这个问题。
所以关于拉你知道你要使用的相关logging的福音是使用: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 是认识到性能问题,除了在某些情况下不join?
看来, :include
function已经改变了Rails 2.1。 在所有情况下,Rails都用来进行连接,但出于性能方面的原因,在某些情况下,它被改为使用多个查询。 Fabio Akita的这篇博客文章有一些关于这个变化的好消息(参见“Optimized Eager Loading”部分)。
.joins
只会join表格,并返回选定的字段。 如果您在联接查询结果上调用关联,则会再次触发数据库查询
:includes
将加载包含的关联并将其添加到内存中。 :includes
加载所有包含的表格属性。 如果您在包含查询结果上调用关联,则不会触发任何查询
我的博客文章有一些详细的解释
连接和包含的区别在于,使用include语句会生成一个更大的SQL查询,将其他表中的所有属性加载到内存中。
例如,如果你有一个充满评论的表,并且使用:joins =>用户为了sorting目的而提取所有的用户信息等等,它可以正常工作,所花费的时间比includeless,但是你想要显示注释以及用户名称,电子邮件等。要使用连接来获取信息,必须为每个用户提取单独的SQL查询,而如果您使用了:include,则可以使用此信息。
很好的例子:
除了性能考虑,还有一个function差异。 当您join评论时,您正在要求提供具有评论的post – 默认情况下为内部联合。 当你包括评论,你要求所有职位 – 外部联接。
我最近阅读更多的差异:joins
和:includes
在rails。 这里是我理解的解释(用例子:))
考虑这种情况:
-
用户has_many评论和评论belongs_to用户。
-
用户模型具有以下属性:名称(string),年龄(整数)。 注释模型具有以下属性:Content,user_id。 对于一个user_id可以是空的。
连接:
:连接在两个表之间执行内部连接 。 从而
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表)的所有logging。 因此,如果你这样做
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
将在后台再次触发数据库查询以获取结果
包括:
:包括执行两个表之间的左外连接 。 从而
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">]>
将导致与评论表中的所有logging的连接表。 因此,如果你这样做
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">]>
它将获取logging,其中comments.user_id是零,如图所示。
还包括加载内存中的两个表。 因此,如果你这样做
comment_1 = Comment.includes(:user).first comment_1.user.age #=> 24
正如你可以注意到,comment_1.user.age只是从内存中加载结果,而不会在后台触发数据库查询。
.joins作为数据库连接工作,它连接两个或多个表,并从后端(数据库)中获取选定的数据。
包括作为数据库的左连接工作。 它装载了左侧的所有logging,没有右侧模型的相关性。 它用于加载,因为它加载所有关联的对象在内存中。 如果我们在包含查询结果上调用关联,那么它不会触发对数据库的查询,它只是从内存中返回数据,因为它已经将数据加载到内存中。
TL;博士
我用两种方式对比他们:
连接 – 用于条件selectlogging。
包括 – 在结果集的每个成员上使用关联时。
更长的版本
连接是为了过滤来自数据库的结果集。 你用它来在你的桌子上进行设置操作。 把它看作是执行集合论的where子句。
Post.joins(:comments)
是相同的
Post.where('id in (select post_id from comments)')
除了如果有多个评论,你会得到与连接重复的post。 但是每一篇文章都会有一个有意见的post。 你可以用不同的方法来纠正它:
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
当你要在一个集合的每个成员上使用一个关系。
刚刚用来连接表的“连接”,当您在连接上调用关联时,它将再次触发查询(这意味着许多查询将触发)
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
但是“包含”会急于加载包含的关联并将其添加到内存中(首次载入所有关联时)并且不会再次触发查询
当你得到logging与包括像logging= User.includes(:组织)。其中(“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}没有查询将触发