通过孩子的孩子数量快速对模型进行排序

时间:2016-07-01 15:02:57

标签: ruby-on-rails-4 activerecord

我目前有以下型号:MinorCategory>产品>评论 在视图中,我显示了评论最多的12个MinorCategories。这个视图的响应速度很慢,我认为这是我查询的一个问题。

这是我目前的代码:

class MinorCategory < ActiveRecord::Base
  has_many :products
  has_many :reviews, through: :products
  ...
  def count_reviews
    self.reviews.count
  end
  ...
end
class Review < ActiveRecord::Base
  belongs_to :product, touch: true
  ...
end
class HomeController < ApplicationController
  @categories = MinorCategory.all.sort_by(&:count_reviews).reverse.take(12)
end

基本上就是这样。在视图中我会浏览每个@categories并显示一些内容,但控制器中的查询似乎很慢。来自SkyLight:

  

SELECT COUNT(*)FROM“review”INNER JOIN“products”ON“review”。“product_id”=“products”。“id”WHERE“products”。“minor_category_id”=? ...平均472ms

我对sql或活动记录不太满意,对Ruby on Rails来说还不是很新。我花了几个小时尝试其他方法,但我无法让它们工作,所以我想我会在这里查看。

提前感谢任何有片刻的人。

1 个答案:

答案 0 :(得分:0)

您需要一些基本的SQL知识才能更好地了解数据库查询的工作方式,以及如何利用DBMS。使用ActiveRecord不是不学习SQL的借口。

也就是说,您的查询效率非常低,因为您根本不会使用数据库的强大功能。在Ruby环境和数据库环境中浪费资源。

唯一的数据库查询是

MinorCategory.all

提取所有记录。这是非常昂贵的,特别是如果你有大量的类别。

此外,self.reviews.count效率很低,因为它受N+1 query issue的影响。

最后但并非最不重要的是,排序和限制是在Ruby环境中进行的,而您应该在数据库中进行排序和限制。

您可以通过利用数据库计算功能轻松获得更有效的查询。您需要将两个表连接在一起。查询应如下所示:

SELECT
    minor_categories.*, COUNT(reviews.id) AS reviews_count
FROM
    "minor_categories" INNER JOIN "reviews" ON "reviews"."minor_category_id" = "minor_categories"."id"
GROUP BY
    minor_categories.id
ORDER BY
    reviews_count DESC
LIMIT 10

在ActiveRecord中转换为

categories = MinorCategory.select('minor_categories.*, COUNT(reviews.id) AS reviews_count').joins(:reviews).order('reviews_count DESC').group('minor_categories.id').limit(10)

您可以使用reviews_count

访问单个类别计数
# take a category 
category = categories[0]
category.reviews_count

另一种不需要JOIN的方法是将计数器缓存在类别表中。