我正在尝试编写一个获取所有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
答案 0 :(得分:2)
我相信你可以用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
此追踪后,确保在创建新购买的已删除邮件时保持purchase_count更新。
现在您可以使用这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)
答案 1 :(得分:0)
分别执行
ids = Purchase.unscoped.uniq.pluck(:user_id)
status[:purchased] = ids.count
ids = User.where.not(id: ids).where(has_registered: true).pluck(:id)
status[:registered] = ids.count
status[:unsubscribed] = User.where.not(id: ids).where(has_unsubscribed: true).count
答案 2 :(得分:0)
这是我的建议,
将一个integer类型的user_status列添加到您的用户模型中。
将数据从has_registered和has_unsubscribed列以及purchases.count迁移到新的user_status列。例如,如果purchases.count> 0然后他们的user_status将是1.如果has_registered,则user_status为0 ....
在您的用户模型中添加:
enum user_status:[:已注册,:已购买,:取消订阅]
然后您可以使用以下方式查询并获取计数:
User.group(:user_status).Count之间的
这将返回类似{1 =&gt; 3,2 =&gt; 1}的内容,其中1是user_status,在这种情况下是“purchase”,3是计数。因此,在此示例中,我们有3个已购买状态的用户和1个已取消订阅的用户。
您也可以使用此方法进行三次查询。
User.purchased.count
User.registered.count
User.unregistere.count