post
有likers
和comments
个孩子。我想根据它们对帖子进行排序。
class Post < ApplicationRecord
scope :latest, -> {
all.sort_by(&:ranking)
}
def ranking
likers.count + comments.count
end
end
这会调用以下查询:
Post Load (0.7ms) SELECT "posts".* FROM "posts"
(0.4ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 52]]
(0.4ms) SELECT COUNT(*) FROM "users" INNER JOIN "user_post_likes" ON "users"."id" = "user_post_likes"."user_id" WHERE "user_post_likes"."post_id" = $1 [["post_id", 52]]
(0.2ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 53]]
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "user_post_likes" ON "users"."id" = "user_post_likes"."user_id" WHERE "user_post_likes"."post_id" = $1 [["post_id", 53]]
所以我尝试以下方法:
Post.includes(:comments, :likers).all.sort_by(&:ranking)
这会调用以下查询:
Post Load (0.7ms) SELECT "posts".* FROM "posts"
Comment Load (0.4ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" IN (52, 53, 54, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71)
UserPostLike Load (0.3ms) SELECT "user_post_likes".* FROM "user_post_likes" WHERE "user_post_likes"."post_id" IN (52, 53, 54, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71)
User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 46
(0.3ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 52]]
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "user_post_likes" ON "users"."id" = "user_post_likes"."user_id" WHERE "user_post_likes"."post_id" = $1 [["post_id", 52]]
(0.2ms) SELECT COUNT(*) FROM "comments" WHERE "comments"."post_id" = $1 [["post_id", 53]]
(0.3ms) SELECT COUNT(*) FROM "users" INNER JOIN "user_post_likes" ON "users"."id" = "user_post_likes"."user_id" WHERE "user_post_likes"."post_id" = $1 [["post_id", 53]]
为什么会发生这种情况,我该如何解决?
更新
我想出了如何解决它,但是一个非常好的解释的答案会很好:
我必须将count
替换为size
。
初始
class Post < ApplicationRecord
scope :latest, -> {
all.sort_by(&:ranking)
}
def ranking
likers.count + comments.count
end
end
后:
class Post < ApplicationRecord
...
def ranking
likers.size + comments.size
end
end
然后,N+1 Query
消失了。我得到的结论是,当你使用counter_cache
时,会发生同样的事情。在这种情况下,我没有使用counter_cache
,但我仍然必须使用size
而不是count
。我假设调用count
强制Rails调用COUNT
SQL查询并调用size
使其使用内存中加载的记录。
答案 0 :(得分:2)
你可以这样使用eager_load
:
Post.eager_load(:comments, :likers).sort_by(&:ranking)
急切加载使用LEFT OUTER JOIN
在单个查询中加载所有关联。
答案 1 :(得分:0)
这里的问题有两个方面:
首先,sort_by
立即为我举起一面旗帜:
http://apidock.com/ruby/Array/sort%21
这是一个Array
方法,意味着您不再构建ActiveRecord查询,而是进行数组转换。
由于您包含comments
和likers
,因此查询并不尽如人意,但这是另一个问题。
.count
的工作方式是预制计数查询SELECT * FROM table
。
要获得所需的结果,您需要构建自己的计数和排序查询。
看一下这篇文章,希望这会让你更好地了解如何进一步优化这个: Rails 3 ActiveRecord: Order by count on association
答案 2 :(得分:0)
在您的情况下,最好的方法是使用counter_cache
likers
和comments
。
您可以阅读更多细节SHORT ARTICLE。这很简单,也是安全的时间和记忆。
如果您使用counter_cache
,则不应向您的数据库发出多个请求。现在你的方法将是:
def ranking
likers_count + comments_count
end
另一方面,如果您不想向表中添加列,请使用includes
:
class Post < ApplicationRecord
scope :latest, -> {
includes(:likers, :comments).sort_by(&:ranking)
}
def ranking
likers.count + comments.count
end
end
但在这种情况下,每当方法调用时,您都会计算likers
和comments