Rails:分组求和并累加往期结果(斐波那契式累加)
实现月度累计报价金额统计
你现在用Groupdate的group_by_month已经能拿到每个月的单独报价总和了,要实现累计求和(累加往期结果),可以分两种方式来做,根据你的数据量和需求选择:
方法一:Ruby层面计算累计(简单易读)
先获取每个月的单独总和,再遍历计算累计值,这种方式逻辑直观,适合数据量不大的场景:
# 1. 获取原始月度分组数据(每个月的单独报价总和) monthly_totals = current_user.quotes.group_by_month(:created_at, last: 12).sum(:price) # 2. 确保数据按月份升序排列(Groupdate默认按时间顺序返回,但手动排序更稳妥) sorted_monthly = monthly_totals.sort_by { |month, _| month }.to_h # 3. 遍历计算累计总和 cumulative_totals = sorted_monthly.each_with_object({}) do |(month, total), result| # 取上一个月的累计值,没有则默认0 previous_total = result.values.last || 0 result[month] = previous_total + total end # 可选:格式化货币(用Rails内置的辅助方法) formatted_results = cumulative_totals.transform_values { |amount| number_to_currency(amount, unit: '€', format: '%n%u') }
执行后formatted_results就会输出你想要的格式,比如:{ Tue, 01 Jan 2024 => "300€", Thu, 01 Feb 2024 => "500€", Fri, 01 Mar 2024 => "600€" }
方法二:数据库层面用窗口函数(高效适合大数据)
如果你的报价数据量较大,在数据库层面用窗口函数计算累计会更高效,无需把所有数据加载到Ruby内存中处理:
cumulative_totals = current_user.quotes .select( "date_trunc('month', created_at) AS month", "SUM(price) OVER (ORDER BY date_trunc('month', created_at)) AS cumulative_price" ) .group("date_trunc('month', created_at)") .order("month") .last(12) # 取最近12个月的数据 .to_h { |row| [row.month, row.cumulative_price] } # 同样可以格式化货币 formatted_results = cumulative_totals.transform_values { |amount| number_to_currency(amount, unit: '€', format: '%n%u') }
这里用PostgreSQL的SUM() OVER (ORDER BY ...)窗口函数,会自动按月份顺序累加每个月的报价总和,性能比Ruby层面处理提升明显。
注意事项
- Groupdate的
group_by_month默认按UTC时间分组,如果你的业务使用本地时间,记得加上time_zone: :local参数,比如group_by_month(:created_at, last: 12, time_zone: :local) - 若使用MySQL,需把
date_trunc('month', created_at)替换为DATE_FORMAT(created_at, '%Y-%m-01'),窗口函数的逻辑保持不变。
内容的提问来源于stack exchange,提问作者anon
相关产品推荐
相关产品推荐

