如何在Active Record中执行分组求和查询并返回分类表多字段
多对多关联下的Category筛选与聚合查询方案
需求说明
Category与Expense通过has_and_belongs_to_many建立多对多关联,需要实现:
- 筛选出存在关联Expense的Category
- 按
categories.id分组 - 返回
categories.name、categories.image_data字段,以及对应关联Expense的金额总和
原尝试问题分析
第一种方法:
Category.includes(:expenses).group('categories.id').sum(:amount)- 问题:ActiveRecord的
sum方法仅返回{id: 总金额}的哈希结构,不会包含Category的其他字段,这是该方法的设计特性。
- 问题:ActiveRecord的
第二种方法:
Category.includes(:expenses).group('categories.id').select('categories.*, SUM(expenses.amount) AS total_amount').pluck(...)- 问题:
includes默认触发预加载(分两次查询:先查Category,再批量查关联Expense),聚合操作在Ruby内存中完成,而非数据库层面,因此数据库返回结果中不存在total_amount别名,导致pluck报错。
- 问题:
第三种方法:
Category.includes(:expenses).group('id', 'name', 'image_data').sum(:amount)- 问题:虽然能返回结果,但按多字段分组会增加数据库计算开销,尤其当表字段较多时性能下降明显;且
includes同样无法保证聚合在数据库层面执行。
- 问题:虽然能返回结果,但按多字段分组会增加数据库计算开销,尤其当表字段较多时性能下降明显;且
正确实现方案
方案1:获取带聚合属性的Category实例
使用joins做内连接(自动过滤无关联Expense的Category),在数据库层面完成聚合,返回的Category实例会附带total_amount属性:
categories_with_total = Category.joins(:expenses) .group('categories.id') .select('categories.*, SUM(expenses.amount) AS total_amount') # 使用示例 categories_with_total.each do |category| puts "#{category.name} - 总金额: #{category.total_amount}" end
方案2:直接获取指定字段的数组结果
如果仅需要特定字段,可直接在pluck中指定聚合表达式,同样基于joins确保数据库层面执行:
result = Category.joins(:expenses) .group('categories.id') .pluck( 'categories.id', 'categories.name', 'categories.image_data', 'SUM(expenses.amount) AS total_amount' ) # 返回结构示例:[[1, "餐饮", "image_bytes", 1500.0], ...]
关键说明
joins(:expenses):内连接会自动排除没有关联Expense的Category,无需额外添加where条件。- 分组仅需按
categories.id(主键)即可:多数数据库(如PostgreSQL、MySQL 5.7+)允许在分组主键时,select其他同表字段,无需将所有字段加入group by,性能更优。
内容的提问来源于stack exchange,提问作者Francis Wayungi
相关产品推荐
相关产品推荐

