我正在尝试编写一个查询,获取所有User状态以及这些状态的计数。然而,我的方法非常昂贵,并且通过进行大量的查询对数据库造成了损失。
我还是个新手,我想重构这段代码,这样它就不会超时了。以下是我目前掌握的代码:
用户型号:

# app/models/user.rb
class User < ApplicationActiveRecordBase
  has_many :purchases
end

购买模式:
# app/models/purchase.rb
class Purchase < ApplicationActiveRecordBase
  belongs_to :user
end

架构:
# app/db/schema.rb
create_table "users", force: :cascade do |t|
  t.boolean  "has_registered"
  t.boolean "has_unsubscribed"
end

create_table "purchases", force: :cascade do |t|
  t.string "item"
  t.integer "price"
  t.integer "user_id"
end

我要优化的代码:
status = Hash.new(0)
User.find_each do |user|
  status[check_user_status(user)] += 1
end

def check_user_status(user)
  if user.purchases.count > 0
    'purchased'
  elsif user.has_registered?
    'registered'
  elsif user.has_unsubscribed?
    'unsubscribed'
  end
end

最佳答案

我相信你可以用3个查询来解决这个问题,如果你只做以下操作,就不需要迭代了:
首先,必须为每个用户的购买保留一个计数器缓存。幸运的是,Rails已经有了一个非常好和优雅的方法来完成它。看看http://railscasts.com/episodes/23-counter-cache-column
因此,您必须更改模型:

# app/models/purchase.rb
class Purchase < ApplicationActiveRecordBase
  belongs_to :user, counter_cache: :purchases_count
end

然后创建迁移以创建计数器列:
# db/migrate/000_add_purchases_counter_to_user.rb
def self.up
  add_column :users, :purchases_count, :integer, :default => 0

  User.reset_column_information
  User.all.each { |u| User.reset_counters u.id, :purchases }
end

def self.down
  remove_column :users, :purchases_count
end

在此之后,rails将确保在创建新的已删除的采购时保持采购计数的更新。
现在,您可以使用这3个查询来检索所需的数据:
purchased = User.count_by_sql("SELECT COUNT(*) FROM USERS WHERE purchases_counter > 0")
registered = User.count_by_sql("SELECT COUNT(*) FROM USERS WHERE purchases_counter = 0 AND has_registered=?", true)
unsubscribed = User.count_by_sql("SELECT COUNT(*) FROM USERS WHERE purchases_counter = 0 AND has_unsubscribed=?", true)

07-24 18:14
查看更多