在Rails 6中如何按X列去重取最新记录并对Y列求和
解决方案
问题原因
你使用的Table.distinct(:x).sum(:y)不符合预期,是因为Rails的distinct方法仅支持传入布尔值控制是否开启全局去重,传入:x并不会实现「按x字段分组仅保留唯一值」的逻辑,最终生成的SQL会对全表所有行去重,自然会把X=A的旧记录纳入求和范围。
正确实现方式
方案1:窗口函数实现(推荐,兼容PG、MySQL 8.0+等主流现代数据库)
通过ROW_NUMBER窗口函数给每个x分组内的记录按创建时间倒序排名,取排名为1的最新记录后再求和,执行效率最高,适合数据量较大的场景:
# 给每个x分组的记录标注排名,created_at越新排名越靠前 ranked_records = Table.select( "*, ROW_NUMBER() OVER (PARTITION BY x ORDER BY created_at DESC, id DESC) AS row_rk" ) # 仅取每个x的最新记录,再对y求和 sum_y = Table.from(ranked_records, "ranked").where("ranked.row_rk = 1").sum(:y)
补充:排序规则增加
id DESC是为了处理同个x存在多条created_at完全相同的记录的边界场景,确保取到的是最后插入的那条记录。
方案2:分组关联实现(兼容低版本MySQL等不支持窗口函数的数据库)
先查询每个x对应的最新创建时间,再关联回原表匹配到对应记录后求和:
# 先获取每个x对应的最新创建时间 latest_time_per_x = Table.group(:x).select("x, MAX(created_at) AS latest_created") # 关联原表匹配到最新记录后求和 sum_y = Table.joins( "INNER JOIN (#{latest_time_per_x.to_sql}) latest ON tables.x = latest.x AND tables.created_at = latest.latest_created" ).sum(:y)
注意事项
如果你的实际表名就是table(和SQL关键字冲突),编写原生SQL片段时需要给表名加反引号转义,避免语法报错。
结果验证
对你提供的样例数据,两种方案执行后得到的求和结果均为9(去重后保留的记录为A:1、B:5、C:3,求和1+5+3=9),符合需求预期。
内容的提问来源于stack exchange,提问作者jdk
相关产品推荐
相关产品推荐

