如果我这样做
post = Post.find_by_id(post_id, :include => :comments)
进行了两个查询(一个用于帖子数据,另一个用于帖子的评论)。然后当我执行post.comments时,不会再执行另一个查询,因为数据已经被缓存。
是否有一种方法可以只执行一个查询,同时仍然通过post.comments访问评论?
post = Post.find_by_id(post_id, :include => :comments)
进行了两个查询(一个用于帖子数据,另一个用于帖子的评论)。然后当我执行post.comments时,不会再执行另一个查询,因为数据已经被缓存。
是否有一种方法可以只执行一个查询,同时仍然通过post.comments访问评论?
不,没有。这是:include
的预期行为,因为JOIN
方法最终会变得低效。
例如,考虑以下情况:需要选择Post
模型的3个字段,2个字段用于Comment
,并且这篇文章有100条评论。Rails可以运行单个JOIN
查询,如下所示:
SELECT post.id, post.title, post.author_id, comment.id, comment.body
FROM posts
INNER JOIN comments ON comment.post_id = post.id
WHERE post.id = 1
这会返回以下结果表:
post.id | post.title | post.author_id | comment.id | comment.body
---------+------------+----------------+------------+--------------
1 | Hello! | 1 | 1 | First!
1 | Hello! | 1 | 2 | Second!
1 | Hello! | 1 | 3 | Third!
1 | Hello! | 1 | 4 | Fourth!
...96 more...
你已经看到了问题。虽然单查询的JOIN
方法返回你需要的数据,但是它会冗余地返回多次。当数据库服务器将结果集发送给Rails时,每个帖子的ID、标题和作者ID都会被发送100次。现在,假设你对Post
感兴趣的有10个字段,其中8个是文本块。噫。那就是很多数据。从数据库传输数据到Rails确实需要CPU周期和RAM来处理,所以尽量减少数据传输对于使应用程序运行更快、更轻巧非常重要。:joins
来替换:include
才能运行复杂的条件语句,但对于预取关系,Rails采用的:include
方法对性能更好。:joins
是否正确实例化其他对象,或者它是否最终会为每个对象触发另一个查询(尽管它真的不应该这样)。也许值得运行自己的基准测试来找出答案 :/ - MatchuSay you have the following model (where :user
is the foreign reference):
class Item < ActiveRecord::Base
attr_accessible :name, :user_id
belongs_to :user
end
Then executing this (note: the where
part is crucial as it tricks Rails to produce that single query):
@items = Item.includes(:user).where("users.id IS NOT NULL").all
will result in a single SQL query (the syntax below is that of PostgreSQL):
SELECT "items"."id" AS t0_r0, "items"."user_id" AS t0_r1,
"items"."name" AS t0_r2, "items"."created_at" AS t0_r3,
"items"."updated_at" AS t0_r4, "users"."id" AS t1_r0,
"users"."email" AS t1_r1, "users"."created_at" AS t1_r4,
"users"."updated_at" AS t1_r5
FROM "measurements"
LEFT OUTER JOIN "users" ON "users"."id" = "items"."user_id"
WHERE (users.id IS NOT NULL)
post.comments
时,它会执行另一个查询。如果有人能想出答案,我会很感兴趣。 - Mischa