如何用Active Record按关联模型count总和筛选并排序Report模型?
按关联模型的聚合值筛选并排序Report
嘿,我完全懂你遇到的问题——直接用sum(:count)确实只会返回所有关联记录的全局总和,没法按单个Report分组计算。咱们用Active Record的分组、聚合函数就能轻松实现你的需求,下面一步步来:
1. 基础需求:按每个Report的count总和排序
要计算每个Report对应的report_day_count的count总和并排序,你需要用group按Report的id分组,再结合select和聚合函数SUM来计算总和:
Report.joins(:report_day_counts) .select('reports.*, SUM(report_day_counts.count) AS total_count') .group('reports.id') .order('total_count DESC')
joins(:report_day_counts):关联两个表,确保只查询有至少一条关联记录的Reportselect(...):查询Report的所有字段,同时计算每个分组的count总和并命名为total_countgroup('reports.id'):按Report的主键分组,这样每个分组对应一个独立的Reportorder('total_count DESC'):按总和从大到小排序,改成ASC就是从小到大
2. 筛选总和满足条件的Report
如果要筛选出总和大于/小于某个值的Report,得用having子句(因为是对聚合后的结果筛选,不能用where):
比如筛选总和大于100的Report:
Report.joins(:report_day_counts) .select('reports.*, SUM(report_day_counts.count) AS total_count') .group('reports.id') .having('SUM(report_day_counts.count) > 100') .order('total_count DESC')
3. 包含没有关联记录的Report
如果你的业务需要包含那些没有任何report_day_count关联的Report(它们的总和为0),就把joins换成left_outer_joins,并用COALESCE把null值转换成0:
Report.left_outer_joins(:report_day_counts) .select('reports.*, COALESCE(SUM(report_day_counts.count), 0) AS total_count') .group('reports.id') .order('total_count DESC')
4. 封装成Scope方便复用
如果这个逻辑会多次用到,建议把它封装成Report模型里的scope,调用起来更简洁:
class Report < ApplicationRecord has_many :report_day_counts # 计算每个Report的total_count scope :with_total_count, -> { left_outer_joins(:report_day_counts) .select('reports.*, COALESCE(SUM(report_day_counts.count), 0) AS total_count') .group('reports.id') } # 按total_count排序,默认降序 scope :ordered_by_total_count, ->(direction = :desc) { with_total_count.order("total_count #{direction}") } # 筛选总和大于指定值的Report scope :total_count_greater_than, ->(value) { with_total_count.having('SUM(report_day_counts.count) > ?', value) } end
之后你就可以这样调用:
# 按总和降序排列所有Report(含无关联的) Report.ordered_by_total_count # 筛选总和大于50的Report并按升序排列 Report.total_count_greater_than(50).ordered_by_total_count(:asc)
内容的提问来源于stack exchange,提问作者user2320239
相关产品推荐
相关产品推荐

