我对rails& amp;我遇到了这个性能问题,我将不胜感激。
我有一个用户模型&每个用户has_many UserScores关联。我正在准备一个显示不同用户统计信息的仪表板,包括基于特定条件的user_scores计数。以下是代码片段:
def dashboard
@users = Array.new
users = User.order('created_at ASC')
users.each do |u|
user = {}
user[:id] = u.id
user[:name] = u.nickname
user[:email] = u.email
user[:matches] = u.user_scores.count
user[:jokers_used] = u.user_scores.where(:joker => true).length
user[:jokers] = u.joker
user[:bonus] = u.user_scores.where(:bonus => 1).length
user[:joined] = u.created_at.strftime("%y/%m/%d")
if user[:matches] > 0
user[:last_activity] = u.user_scores.order('updated_at DESC').first.updated_at.strftime("%y/%m/%d")
else
user[:last_activity] = u.updated_at.strftime("%y/%m/%d")
end
@users << user
end
@user_count = @users.count
端
我看到的问题是对每个用户重复UserScore db查询以获取不同的计数。
有没有办法避免那些多次查询?
N.B。我不确定我为视图准备数据的方法是否是最佳方式,因此任何有关该方法的建议或提示都将受到高度赞赏。
由于
答案 0 :(得分:1)
使用include方法来急切加载你有很多关联。您可以在此处理解此概念:https://www.youtube.com/watch?v=s2EPVMqOsTQ
答案 1 :(得分:1)
您需要急切加载users_scores以减少多个查询。 @ Slava.K就如何消除它提供了很好的解释。
添加includes(:user_scores)
用于查询用户,并使用ruby的方法在从DB通过查询获取数据后使用集合。
请参阅以下代码以了解:
users = User.includes(:user_scores).order('created_at ASC')
users.each do |u|
....
user[:matches] = u.user_scores.length
user[:jokers_used] = u.user_scopes.select{ |score| score.joker == true }.length
user[:jokers] = u.joker
user[:bonus] = u.user_scores.select{ |score| score.bonus == 1 }.length
....
end
此外,您准备回应的方式并不干净灵活。相反,您应该重写as_json
方法来准备json,它可以正确地使用视图。默认情况下为模型定义as_json
方法。您可以从官方文档http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html中了解更多相关信息,或访问article on preparing clean json response,其中我解释了正确深入覆盖as_json。
答案 2 :(得分:0)
首先,在您的查询中引用user_scores
关联:
users = User.includes(:user_scores).order('created_at ASC')
跟踪铁路文档协会渴望加载:http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
另请注意,即使您的关联已经预加载,where
也会对数据库进行新查询。因此,而不是
u.user_scores.where(:joker => true).length
u.user_scores.where(:bonus => 1).length
尝试:
u.user_scores.count { |us| us.joker }
u.user_scores.count { |us| us.bonus == 1 }
您可能还必须以某种方式重写.user_scores.order('updated_at DESC').first.updated_at.strftime("%y/%m/%d")