Rails :include 与 :joins

发布于 2024-07-29 03:42:11 字数 1049 浏览 2 评论 0原文

这更多的是一个“为什么事情会这样工作”的问题,而不是一个“我不知道如何做到这一点”的问题......

因此,提取您知道将要使用的关联记录的福音是使用 :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

我错过了什么吗? 我有一个包含六个关联的应用程序,我在一个屏幕上显示所有关联的数据。 似乎使用 1 个连接查询比 6 个个体更好。 我知道从性能角度来看,进行联接并不总是比单独查询更好(事实上,如果您按照花费的时间计算,看起来上面的两个单独查询比联接更快),但毕竟文档我一直在阅读,我很惊讶地发现 :include 没有像广告中那样工作。

也许 Rails 意识到了性能问题并且除了某些情况外不会加入?

This is more of a "why do things work this way" question rather than a "I don't know how to do this" question...

So the gospel on pulling associated records that you know you're going to use is to use :include because you'll get a join and avoid a whole bunch of extra queries:

Post.all(:include => :comments)

However when you look at the logs, there's no join happening:

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) 

It is taking a shortcut because it pulls all of the comments at once, but it's still not a join (which is what all the documentation seems to say). The only way I can get a join is to use :joins instead of :include:

Post.all(:joins => :comments)

And the logs show:

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

Am I missing something? I have an app with half a dozen associations and on one screen I display data from all of them. Seems like it would be better to have one join-ed query instead of 6 individuals. I know that performance-wise it's not always better to do a join rather than individual queries (in fact if you're going by time spent, it looks like the two individual queries above are faster than the join), but after all the docs I've been reading I'm surprised to see :include not working as advertised.

Maybe Rails is cognizant of the performance issue and doesn't join except in certain cases?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

芯好空 2024-08-05 03:42:11

Rails 2.1 似乎改变了 :include 功能。 Rails 过去在所有情况下都会进行连接,但出于性能原因,它被更改为在某些情况下使用多个查询。 这篇博文Fabio Akita 的 提供了有关此更改的一些很好的信息(请参阅标题为“优化的预加载”的部分)。

It appears that the :include functionality was changed with Rails 2.1. Rails used to do the join in all cases, but for performance reasons it was changed to use multiple queries in some circumstances. This blog post by Fabio Akita has some good information on the change (see the section entitled "Optimized Eager Loading").

撧情箌佬 2024-08-05 03:42:11

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

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

.joins will just join the tables and brings selected fields in return. if you call associations on joins query result, it will fire database queries again

:includes will eager load the included associations and add them in memory. :includes loads all the included tables attributes. If you call associations on include query result, it will not fire any queries

嗫嚅 2024-08-05 03:42:11

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

例如,如果您有一个充满注释的表,并且您使用 :joins =>; 用户提取所有用户信息以进行排序等,它会工作得很好,并且比 :include 花费的时间更少,但假设您想要显示评论以及用户名、电子邮件等。要使用 :joins 获取信息,它必须为它获取的每个用户进行单独的 SQL 查询,而如果您使用 :include ,则此信息可供使用。

很好的例子:

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

The difference between joins and include is that using the include statement generates a much larger SQL query loading into memory all the attributes from the other table(s).

For example, if you have a table full of comments and you use a :joins => users to pull in all the user information for sorting purposes, etc it will work fine and take less time than :include, but say you want to display the comment along with the users name, email, etc. To get the information using :joins, it will have to make separate SQL queries for each user it fetches, whereas if you used :include this information is ready for use.

Great example:

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

瀟灑尐姊 2024-08-05 03:42:11

我最近阅读了有关 Rails 中 :joins:includes 之间差异的更多内容。 以下是我的理解的解释(带有示例:))

考虑这种情况:

  • 一个用户有很多评论,并且一个评论属于一个用户。

  • 用户模型具有以下属性:Name(字符串)、Age(整数)。 Comment模型有以下属性:Content、user_id。 对于评论,user_id 可以为 null。

联接:

:联接在两个表之间执行内联接。 因此,

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:

:includes 执行 left两个表之间的外部联接。 因此,

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

将产生一个包含 comments 表中所有记录的连接表。因此,如果您这样做,

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 只是从内存中加载结果,而无需在后台触发数据库查询。

I was recently reading more on difference between :joins and :includes in rails. Here is an explaination of what I understood (with examples :))

Consider this scenario:

  • A User has_many comments and a comment belongs_to a User.

  • The User model has the following attributes: Name(string), Age(integer). The Comment model has the following attributes:Content, user_id. For a comment a user_id can be null.

Joins:

:joins performs a inner join between two tables. Thus

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

will fetch all records where user_id (of comments table) is equal to user.id (users table). Thus if you do

Comment.joins(:user).where("comments.user_id is null")

#=> <ActiveRecord::Relation []>

You will get a empty array as shown.

Moreover joins does not load the joined table in memory. Thus if you do

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

As you see, comment_1.user.age will fire a database query again in the background to get the results

Includes:

:includes performs a left outer join between the two tables. Thus

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

will result in a joined table with all the records from comments table. Thus if you do

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

it will fetch records where comments.user_id is nil as shown.

Moreover includes loads both the tables in the memory. Thus if you do

comment_1 = Comment.includes(:user).first

comment_1.user.age
#=> 24

As you can notice comment_1.user.age simply loads the result from memory without firing a database query in the background.

溺深海 2024-08-05 03:42:11

除了性能考虑之外,还存在功能差异。
当您加入评论时,您是在请求有评论的帖子 - 默认情况下是内部联接。
当您添加评论时,您会要求所有帖子 - 外部联接。

In addition to a performance considerations, there's a functional difference too.
When you join comments, you are asking for posts that have comments- an inner join by default.
When you include comments, you are asking for all posts- an outer join.

神妖 2024-08-05 03:42:11

tl;dr

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

连接 - 用于有条件地选择记录。

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

较长版本

连接旨在过滤来自数据库的结果集。 您可以使用它在表上执行集合操作。 将其视为执行集合论的 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 次查询)

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

在 也就是说,当您想要执行条件集合操作时,请使用joins;当您要在集合的每个成员上使用关系时,请使用includes

tl;dr

I contrast them in two ways:

joins - For conditional selection of records.

includes - When using an association on each member of a result set.

Longer version

Joins is meant to filter the result set coming from the database. You use it to do set operations on your table. Think of this as a where clause that performs set theory.

Post.joins(:comments)

is the same as

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

Except that if there are more than one comment you will get duplicate posts back with the joins. But every post will be a post that has comments. You can correct this with distinct:

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

In contract, the includes method will simply make sure that there are no additional database queries when referencing the relation (so that we don't make n + 1 queries)

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

The moral is, use joins when you want to do conditional set operations and use includes when you are going to be using a relation on each member of a collection.

旧人九事 2024-08-05 03:42:11

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

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

.joins works as database join and it joins two or more table and fetch selected data from backend(database).

.includes work as left join of database. It loaded all the records of left side, does not have relevance of right hand side model. It is used to eager loading because it load all associated object in memory. If we call associations on include query result then it does not fire a query on database, It simply return data from memory because it have already loaded data in memory.

何止钟意 2024-08-05 03:42:11

“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}
不会触发任何查询

'joins' just used to join tables and when you called associations on joins then it will again fire query (it mean many query will fire)

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)

total number of SQL is 11 in this case

But with
'includes' will eager load the included associations and add them in memory(load all associations on first load) and not fire query again

when you get records with includes like
@records= User.includes(:organisations).where("organisations.user_id = 1")
then query will be

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}
no query will fire

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文