Rails 条件更好

Better Where Conditions With Rails

在我的 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 查询,而且看起来效率不高。有没有更好的方法,更好的方法来处理这个问题?

您可以从提取循环内的各种计数查询开始,然后利用 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

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

使用单个 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 将您的活动记录链分解为可重用的对象。