1

楷模:

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

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

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

4

2 回答 2

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 月份创建新条目时简单地将其过期。我想说您应该先使用这种方法,然后再尝试单独缓存每个类别的条目。此查询应该执行得很快,因此您不必深入研究单独缓存每一行。它还将执行单个查询,而不是为每个类别执行一个查询。

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

  • 您仍然必须查询数据库以获取用户的类别列表或类别 ID,因此无论如何您都必须执行一个查询。
  • 缓存过期更复杂,因为在更多情况下您必须使两个缓存过期,例如,当条目的类别更改时,您必须使旧类别缓存和新类别缓存过期。
  • 您最终可能会针对您的数据库运行更多查询以获取过期的缓存信息,并且数据库的延迟最终可能会比实际查询花费更长的时间。
  • 您不需要缓存每一行,因为查询很简单并且使用索引。您应该有一个关于 user_id 和 category_id 条目的索引。
于 2011-04-08T16:03:52.673 回答
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')
}
于 2011-04-10T09:58:41.190 回答