预先缓存初始查询,以使子查询不会到达数据库

时间:2016-05-24 00:13:55

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

我有两个表,UsersResponses,每个用户有很多响应。我正在寻找对每个用户的响应集进行一些计算。类似下面的代码:

all_users = User.all
all_rs = Responses.all

all_users.map { |u| all_rs.where(user: u).count }

如上所述,这将为每个用户调用数据库。有没有办法预先缓存all_rs数据,以便后续的每个where都在内存中完成?

上面的逻辑可能很容易用sql编写,但想象一下map块中的代码包含了更多的工作。

3 个答案:

答案 0 :(得分:1)

您需要的是计数器缓存(请参阅Rails Guide的第4.1.2.3节)。

要启用计数器缓存,首先,添加或更改迁移文件:

<强>分贝/迁移/ add_counter_cache_to_users.rb

class AddCounterCacheToUsers < ActiveRecord::Migration
  def change
    # Counter cache field
    add_column :users, :responses_count, :integer, null: false, default: 0
    # Optionally add index to the column if you want to `order by` it.
    add_index :users, :responses_cache
  end
end

然后修改模型类

应用/模型/ response.rb

class Response < ActiveRecord::Base
  belongs_to :user, counter_cache: true
end

运行rake db:migrate。从现在开始,每当创建Response时,users.responses_count列的值将自动递增1,每当Response被销毁时,该列的值将减1。

如果您想要计算某人的回复,请拨打该用户的responses_count

user = User.first
user.responses_count  #=> the responses count of the user
# or
user.responses.size

您的原始要求可以通过

来实现
User.select(:responses_count).to_a

更新

我想不出我怎么能错过这么简单的血腥解决方案

Response.group(:user_id).count

答案 1 :(得分:1)

您可以执行类似

的操作
responses_by_user = Response.all.group_by(&:user_ud)

(假设Response具有user_id属性)

然后,您可以执行responses_by_user[user.id]以获取用户的响应,而无需进一步查询。请注意创建所有这些额外的ActiveRecord对象的开销。正如您所暗示的那样,您提供的非常具体的示例可以通过sql group / count来处理,这可能要快得多。

答案 2 :(得分:0)

这样的事可能吗?假设用户有很多回复

all_users = User.includes(:responses)
user_responses_count = all_users.map { |u| u.responses.count }

如果您有1000个用户,则不会查询响应1000次。如果这适用于您的用例,请告诉我。

请注意此查询级别缓存。不需要更改模型..