Ruby-on-rails Rails :include 与 :joins

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1208636/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:34:57  来源:igfitidea点击:

Rails :include vs. :joins

ruby-on-railsrubydatabasejoinrails-activerecord

提问by Rob Cameron

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 :includebecause you'll get a join and avoid a whole bunch of extra queries:

因此,提取您知道将要使用的关联记录的福音是使用,:include因为您将获得连接并避免一大堆额外的查询:

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 istaking 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 :joinsinstead of :include:

走了一条捷径,因为它拉的所有注释的一次,但它仍然不是一个连接(这是所有的文件似乎说什么)。我可以加入的唯一方法是使用:joins而不是: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 :includenot working as advertised.

我错过了什么吗?我有一个包含六个关联的应用程序,我在一个屏幕上显示所有这些关联的数据。似乎有一个加入的查询而不是 6 个个人会更好。我知道在性能方面,执行连接而不是单个查询并不总是更好(实际上,如果您按所花费的时间计算,上面的两个单独查询似乎比连接快),但毕竟是文档我一直在阅读我很惊讶地发现它:include没有像宣传的那样工作。

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

也许Rails的认识到性能问题,并除非在某些情况下,不加入呢?

采纳答案by Greg Campbell

It appears that the :includefunctionality 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 postby Fabio Akita has some good information on the change (see the section entitled "Optimized Eager Loading").

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

回答by Prem

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

.joins将只是加入表格并带来选定的字段作为回报。如果您在连接查询结果上调用关联,它将再次触发数据库查询

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

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

回答by holden

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).

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

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.

例如,如果您有一个充满评论的表格,并且您使用 :joins => users 来提取所有用户信息以进行排序等,那么它将工作正常并且比 :include 花费的时间更少,但是假设您想显示评论以及用户名、电子邮件等。要使用 :joins 获取信息,它必须为它获取的每个用户进行单独的 SQL 查询,而如果使用 :include 则此信息已准备好使用。

Great example:

很好的例子:

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

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

回答by Brian Maltzan

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.

除了性能方面的考虑,还有功能上的差异。当您加入评论时,您要求有评论的帖子 - 默认情况下是内部加入。当您包含评论时,您要求所有帖子 - 外部联接。

回答by Aaditi Jain

I was recently reading more on difference between :joinsand :includesin rails. Here is an explaination of what I understood (with examples :))

我最近阅读了更多关于Rails:joins:includesRails之间的区别。这是我所理解的解释(带有示例:))

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.

  • 一个用户有_许多条评论,一个评论属于一个用户。

  • User 模型具有以下属性:Name(string)、Age(integer)。Comment 模型具有以下属性:Content、user_id。对于评论,user_id 可以为空。

Joins:

加入:

:joins performs a inner joinbetween two tables. Thus

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

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

将获取user_id(评论表)等于 user.id(用户表)的所有记录。因此,如果你这样做

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

此外,joins 不会在内存中加载已连接的表。因此,如果你这样做

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

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

如您所见,comment_1.user.age将在后台再次触发数据库查询以获取结果

Includes:

包括:

:includes performs a left outer joinbetween the two tables. Thus

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

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.

它将获取comments.user_id为零的记录,如图所示。

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.

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

回答by Kevin Choubacha

tl;dr

tl;博士

I contrast them in two ways:

我从两个方面对比它们:

joins- For conditional selection of records.

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

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.

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

Post.joins(:comments)

Post.joins(:comments)

is the same as

是相同的

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

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 includesmethod will simply make sure that there are no additional database queries when referencing the relation (so that we don't make n + 1 queries)

在合同中,该includes方法将简单地确保在引用关系时没有额外的数据库查询(这样我们就不会进行 n + 1 次查询)

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

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

寓意是,joins当您想要执行条件集合操作时使用includes,并在您要对集合的每个成员使用关系时使用。

回答by Kevin Choubacha

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

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

.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.

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

回答by Thorin

'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)

'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)

total number of SQL is 11 in this case

在这种情况下,SQL 总数为 11

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

当您获得包含 @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} no query will fire

@records.map{|u|u.organisation.name} 不会触发任何查询