我正在尝试编写一个查询,获取所有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)