如何使用多个类别的总和优化ActiveRecord查询

时间:2011-04-08 14:50:18

标签: ruby-on-rails ruby-on-rails-3 activerecord activemodel

型号:

  • 用户has_many类别,has_many条目
  • 类别has_many条目
  • 条目属于类别和用户

我们假设,条目上有名称和金额。 如果我有一个视图需要显示特定用户的特定月份(让我们现在采用created_at),并希望显示一个表格,其中包含按类别分组的特定月份中的所有条目(不显示空类别)和一个总和并计算一个类别中的条目。

我的问题是: 查询数据库(并充分利用缓存)的最有效方法是什么?该视图将经常呈现,并且每次用户创建新条目时,自然地,一个类别的总和会改变,而不会改变其他类别的总和。

2 个答案:

答案 0 :(得分:2)

查询部分可以通过范围完成:

class Category
  scope :grouped_entries, lambda { |user, date|
    select(['categories.*', 'COUNT(entries.id) as count_entries'])
      .joins(:entries)
      .where('entries.user_id = ?', user.id)
      .where('MONTH(entries.created_at) = ?', date.month)
      .group('categories.id')
  }
end

然后可以循环:

<% Category.grouped_entries(current_user, Date.today).each do |category| %>
  <%= category.name %> with <%= category.count_entries %> entries this month.
<% end %>

当然,缓存此操作需要您在本月创建条目时随时刷新缓存。例如,您可以像这样缓存查询:

@categories = Rails.cache.fetch("/grouped_entries/#{current_user.id}/#{Date.today.month}") do
  Category.grouped_entries(current_user, Date.today).all
end

然后在使用user_id和条目created_at month创建新条目时简单地过期。我会说,在尝试单独缓存每个类别的条目之前,应首先使用此方法。此查询应该很快执行,因此您不必深入研究单独缓存每一行。它还将执行单个查询,而不是每个类别一个查询。

这就是为什么我不会单独缓存每一行:

  • 您仍然需要查询数据库以获取用户的类别或类别ID列表,因此您必须执行一个查询。
  • Cachine到期更复杂,因为在更多情况下您必须使两个缓存过期,例如,当条目的类别发生更改时,您必须使旧类别缓存和新类别缓存过期。
  • 您最终可能会针对您的数据库运行更多查询以获取过期的缓存信息,并且数据库的延迟可能最终会花费比实际查询更长的时间。
  • 您不需要缓存每一行,因为查询很简单并且使用索引。您应该在user_id和category_id上有条目索引。

答案 1 :(得分:1)

潘的回答非常完美而且很有帮助。对于档案:这是我在我的应用中使用的查询:

scope :grouped_entries, lambda { |user, date|
  select(['categories.*', 'COUNT(entries.id) as count_entries']).
    joins(:entries).
    where('entries.user_id = ?', user.id).
    where(':first_day <= entries.created_at AND entries.created_at <= :last_day', { 
       :first_day => date.at_beginning_of_month,
       :last_day => date.at_end_of_month
    } ).
    group('categories.id')
}