Rails ActiveRecord查询调用group子句时distinct去重失效问题
问题根源
- 重复行产生原因:你在
includes关联order、product的同时,对orders表加了过滤条件,ActiveRecord会自动将includes转为左连接查询(eager_load模式),多表左连接时如果关联表存在一对多映射,就会导致主表LineItem的记录重复出现。 distinct加group后失效原因:无group时,sum会基于去重后的LineItem行计算;加group后,distinct的作用域变为「分组后的组合行」,不再单独对LineItem主键去重,因此重复记录还是会被多次计入求和结果。
解决方案
方案1:替换includes为preload+joins(推荐,性能最优,无需额外去重)
preload会单独查询关联表做预加载避免N+1问题,不会触发多表连接导致的重复,joins仅用于关联过滤和字段引用,从根源避免重复行产生:
LineItem .where(product_id: 14) .preload(:order, :product) .joins(:order, :product) .where(orders: { order_type: 1, order_date: (Date.today - 1.week)..Time.now }) .group('products.model_number') .sum(:order_quantity)
方案2:子查询先筛选去重的LineItem,再分组求和
如果你确实需要保留连接查询逻辑,可以先过滤出符合条件的唯一LineItem ID,再做后续聚合:
# 第一步:筛选符合条件的去重LineItem ID valid_line_item_ids = LineItem .distinct .where(product_id: 14) .joins(:order) .where(orders: { order_type: 1, order_date: (Date.today - 1.week)..Time.now }) .pluck(:id) # 第二步:基于去重的ID分组求和 LineItem .where(id: valid_line_item_ids) .joins(:product) .group('products.model_number') .sum(:order_quantity)
方案3:PostgreSQL环境下用DISTINCT ON强制按LineItem主键去重
如果你使用PostgreSQL,可以直接在查询层强制按主表ID去重后再聚合:
LineItem .select('DISTINCT ON (line_items.id) line_items.order_quantity, products.model_number') .where(product_id: 14) .joins(:order, :product) .where(orders: { order_type: 1, order_date: (Date.today - 1.week)..Time.now }) .group('products.model_number, line_items.order_quantity') .sum(:order_quantity)
内容的提问来源于stack exchange,提问作者Tyler P
相关产品推荐
相关产品推荐

