Rails group方法在Postgres中计算平均产量结果错误
问题分析与解决方案
我仔细看了你的查询代码、生成的SQL以及模型关联,发现问题的根源在于错误的表关联导致分组计算逻辑混乱。
问题所在
看你生成的SQL语句,里面重复JOIN了两次treatment_selections表,最后关联treatments的时候居然用了:
INNER JOIN "treatments" ON "treatments"."id" = "treatment_selections_trials"."id"
这完全错了!按照你的模型关联,TreatmentSelection关联Treatment应该用treatment_id外键,正确的关联条件应该是:
INNER JOIN "treatments" ON "treatments"."id" = "treatment_selections_trials"."treatment_id"
这个错误导致每个Treatment被错误地关联到所有TreatmentSelection(或者关联到ID相同的TreatmentSelection,这显然不是你要的逻辑),最终分组计算时,每个Treatment的分组都包含了所有产量数据,所以所有结果的平均值都是整体平均。
修正方案
你可以通过两种方式修正这个问题:
方案1:调整查询起点,从TreatmentSelection出发(推荐)
这种写法关联逻辑更清晰,避免重复JOIN:
@search = TreatmentSelection.joins(:trial => [:region, :property], :quality_datum, :treatment) .where("EXTRACT(year from trials.season_year) BETWEEN ? AND ?", params[:start_year], params[:end_year]) .where('regions.name': params[:select_region]) .where('properties.name': params[:select_property]) .group('treatments.id, treatments.name') .select('treatments.name, AVG(quality_data.yield) as yield')
方案2:从Trial出发,调整JOIN的嵌套结构
把quality_datum嵌套到treatment_selections的关联里,避免重复JOIN:
@search = Trial.joins(:region, :property, treatment_selections: [:quality_datum, :treatment]) .where("EXTRACT(year from season_year) BETWEEN ? AND ?", params[:start_year], params[:end_year]) .where('regions.name': params[:select_region]) .where('properties.name': params[:select_property]) .group('treatments.id, treatments.name') .select('treatments.name, AVG(quality_data.yield) as yield')
额外注意点
- 我在GROUP BY里加上了
treatments.name,这是因为PostgreSQL的严格模式要求所有非聚合列都必须出现在GROUP BY中(虽然只GROUP BY主键在某些情况下也能工作,但加上字段更稳妥)。 - 用
?占位符代替字符串拼接(比如原来的#{params[:start_year]}),可以避免SQL注入风险,这是Rails查询的最佳实践。
修正后生成的SQL会正确关联treatments和treatment_selections,分组计算每个Treatment的平均产量,就能得到你预期的结果了。
内容的提问来源于stack exchange,提问作者yellowskull
相关产品推荐
相关产品推荐

