在Rails中重构此方法以防止SQL查询运行两次



使用Ruby 1.9.2, Rails 3.1。我有以下代码:

# review.rb
def calculate_rating(reviewable)
  total_rating = Review.select("rating").where(:reviewable_id => self.reviewable_id)
  sum_rating = total_rating.sum(:rating)
  new_rating_average = sum_rating.to_f / total_rating.size
  reviewable.update_attribute(:rating_average, new_rating_average)
end

total_rating实际用于获得返回的评级数的总数,而sum_rating用于将每个total_rating相加。我注意到我的查询要运行多次才能得到结果。

是否有任何方法重构它来提高性能?

谢谢。

活动记录api公开SQL平均函数,因此您应该能够执行

average_rating = Review.where(:reviewable_id => self.reviewable_id).average(:rating)

一般情况下,您可能需要编写自定义sql,例如,如果平均函数不存在,您可以编写

select count(*) as c, sum(rating) as total from ratings where ( ... )

如果你想在不同的条件下计算多个事物,你可以使用像

这样的东西
select count(*) as number_of_ratings, SUM(IF(rating > 5, 1, 0)) as number_of_ratings_greater_than_5, SUM(rating) as total_score

获取,在1查询评分的个数,它们的和和评分的个数> 5。不过要谨慎使用,你不会想用一个非常慢的查询代替2个快速查询!

假设reviewable有很多评论:

reviewable.update_attribute :rating_average, reviewable.reviews.average(:rating)

或者,如果您的数据库支持语法[1]

Reviewable.connection.execute <<-eos
  UPDATE reviewable SET (rating_average) = ((
    SELECT avg(rating) FROM review WHERE reviewable_id = {reviewable_id}
  )) where id = {reviewable_id}
eos

[1] http://publib.boulder.ibm.com/infocenter/idshelp/v10/index.jsp?topic=/com.ibm.sqls.doc/sqls875.htm