1

モデル:

  • ユーザー has_many カテゴリ、has_many エントリ
  • カテゴリ has_many エントリ
  • エントリーはカテゴリとユーザーの両方に属します。

エントリに名前と金額があると仮定しましょう。特定のユーザーの特定の月を表示する必要があるビューがあり (ここでは created_at を使用します)、その特定の月のすべてのエントリをカテゴリ別にグループ化 (空のカテゴリは表示されません) し、合計を含むテーブルを表示したい場合カテゴリ内のエントリの数。

私の質問は次のとおりです。データベースにクエリを実行する (そしてキャッシュを最大限に活用する) 最も効率的な方法は何ですか? このビューは非常に頻繁にレンダリングされ、ユーザーが新しいエントリを作成するたびに、自然に 1 つのカテゴリの合計が変更されますが、他のカテゴリの合計は変更されません。

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 month を使用して新しいエントリが作成されると、単純に期限切れになります。すべてのカテゴリのエントリを個別にキャッシュする前に、まずこのアプローチを使用する必要があります。このクエリは非常に高速に実行されるため、各行を個別にキャッシュすることを詳しく調べる必要はありません。また、カテゴリごとに 1 つではなく、1 つのクエリを実行します。

各行を個別にキャッシュしない理由は次のとおりです。

  • ユーザーのカテゴリまたはカテゴリ ID のリストを取得するためにデータベースにクエリを実行する必要があるため、とにかく 1 つのクエリを実行する必要があります。
  • キャッシュの有効期限はより複雑です。たとえば、エントリのカテゴリが変更されたときに、古いカテゴリ キャッシュと新しいカテゴリ キャッシュを期限切れにする必要がある場合など、2 つのキャッシュを期限切れにする必要がある場合が多いためです。
  • 期限切れのキャッシュ情報を取得するためにデータベースに対してさらに多くのクエリを実行することになり、データベースへのレイテンシが実際のクエリよりも長くかかる可能性があります。
  • クエリは単純でインデックスを使用するため、すべての行をキャッシュする必要はありません。エントリの 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 に答える