如何在Rails中基于关联Action的最早时间去重排序查询MyModel?
解决方案:数据库层面实现去重并按关联最早Action时间排序
你的需求完全可以在数据库层面完成,不需要在Rails层面做额外处理。当前scope返回重复MyModel的原因是group子句同时包含了my_models.id和actions.time,这会让每个不同的actions.time都生成独立分组,导致同一个MyModel对应多条记录。
修正后的Scope写法
你可以通过分组仅按my_models.id聚合,同时用MIN(actions.time)获取每个MyModel关联的最早Action时间,再基于这个值排序:
scope :my_scope, lambda { joins(:actions) .where.not(actions: { status: :finished }) .select('my_models.*, MIN(actions.time) as earliest_action_time') .group('my_models.id') .order('earliest_action_time ASC') }
说明
select语句中新增MIN(actions.time) as earliest_action_time:计算每个MyModel关联的未完成Action的最早时间,并起别名方便排序group('my_models.id'):仅按MyModel的ID分组,确保每个MyModel只返回一条记录order('earliest_action_time ASC'):基于计算出的最早时间升序排序(如果要降序改成DESC即可)
如果你的Rails版本支持,也可以用更具可读性的Arel写法避免硬写SQL字符串:
scope :my_scope, lambda { joins(:actions) .where.not(actions: { status: :finished }) .select(MyModel.arel_table[Arel.star], Action.arel_table[:time].minimum.as('earliest_action_time')) .group(MyModel.arel_table[:id]) .order(Arel.sql('earliest_action_time ASC')) }
这样就能在数据库层面完成去重和排序,返回的结果是唯一的MyModel记录,且按关联的最早未完成Action时间排序。
内容的提问来源于stack exchange,提问作者Petros Kalafatidis
相关产品推荐
相关产品推荐

