使用Rails更好的条件

时间:2015-01-21 12:00:48

标签: ruby-on-rails ruby-on-rails-4

在我的Rails 4应用程序中,我正在做很多关于模型的查询。这是代码:

@examination.cities.includes(:translations).each do |city|
  Participation.where(exam_center_preference: city.id, payment_status: true, examination_id: @examination.id).count
  Participation.where(exam_center_preference: city.id, payment_status: false, examination_id: @examination.id).count
  Participation.where(exam_center_preference: city.id, examination_id: @examination.id).count

  @examination.exam_languages.each do |exam_language|
    Participation.where(exam_center_preference: city.id, language_preference: exam_language.id, examination_id: @examination.id).count
  end
end

问题是,这段代码生成了太多的SQL查询,看起来效率不高。有没有更好的方法,更好的方法来处理这个?

1 个答案:

答案 0 :(得分:1)

您可以从循环中提取各种计数查询开始,并利用SQL语言通过单个查询执行计数。

例如,如果你采取这个循环

@examination.exam_languages.each do |exam_language|
    Participation.where(exam_center_preference: city.id, language_preference: exam_language.id, examination_id: @examination.id).count
end

并且您应用原则detailed in this answer,您可以在单个查询中执行每种语言的计数。

使用单个SQL语句无法轻松减少其他查询。例如

Participation.where(exam_center_preference: city.id, payment_status: true, examination_id: @examination.id).count
Participation.where(exam_center_preference: city.id, payment_status: false, examination_id: @examination.id).count
Participation.where(exam_center_preference: city.id, examination_id: @examination.id).count

在这种情况下有几种可能的方法。您可以缓存查询,以便在一个范围内只执行一次。

# the query is performed and cached for 1 minute
Rails.cache.fetch("queries/blabla/paid", expires_in: 1.minute) {
  Participation.where(exam_center_preference: city.id, payment_status: true, examination_id: @examination.id).count
}

理想情况下,您应该将这种复杂性隐藏在控制器之外。在控制器中链接很多ActiveRecord范围,它绝对不是一种可重用的方法。它还限制了正确测试查询的能力。

不提及何时开始将查询与其他层(例如缓存层)混合。您可以考虑one of these patterns将活动记录链分解为可重用对象。