Rails下用ActiveRecord接口计算两模型has_many关联的平均数量
纯ActiveRecord实现用户平均铃声数统计
首先区分两种常见业务场景,按需选择对应实现:
场景1:统计所有用户的平均铃声数(包含无铃声的用户)
直接用总铃声数除以总用户数即可,实现最简单、性能最优:
avg_ringtone_count = Ringtone.count.to_f / User.count
场景2:仅统计至少拥有1个铃声的用户的平均铃声数(和你提供的原生SQL逻辑等价)
三种实现可选,按需选取:
方案A:极简双查询实现
# 先获取每个有铃声用户的铃声数集合,再求平均 user_tones_counts = Ringtone.group(:user_id).count.values avg_ringtone_count = user_tones_counts.sum.to_f / user_tones_counts.size
方案B:单查询实现(性能更优,无需二次计算)
通过ActiveRecord构造子查询复用分组统计结果,等价于你写的CTE逻辑,避免硬写完整SQL:
# 构造分组统计子查询 count_subquery = Ringtone.select('user_id, COUNT(id) as tone_count').group(:user_id).to_sql # 基于子查询计算平均值 avg_ringtone_count = ActiveRecord::Base.connection.select_value("SELECT AVG(tone_count) FROM (#{count_subquery}) AS sub_query").to_f
方案C:counter_cache优化方案(适合高频调用场景)
如果该统计需要频繁调用,推荐给User模型加计数器缓存,后续查询仅需扫User表即可:
- 生成迁移给User加
ringtones_count字段:
add_column :users, :ringtones_count, :integer, default: 0, null: false
- 修改Ringtone模型的关联配置开启计数器缓存:
class Ringtone < ApplicationRecord belongs_to :user, counter_cache: true # 其余原有代码 end
- 后续直接调用average方法即可获取结果:
avg_ringtone_count = User.average(:ringtones_count).to_f
注:开启counter_cache后,新增/删除Ringtone记录时Rails会自动更新对应User的
ringtones_count字段,无需手动维护。
内容的提问来源于stack exchange,提问作者kainkainkain
相关产品推荐
相关产品推荐

