Active Record如何按子关联ctasks、legacy_ctasks最早start_time排序Change
问题修复方案
原代码问题
- 使用
left_joins会保留未关联任何ctasks、legacy_ctasks的Change记录,不符合「Change必须至少关联1个ctask或legacy_ctask」的要求 - 排序逻辑为优先按ctasks的start_time升序、其次按legacy_ctasks的start_time升序,未取两类子记录的最小start_time作为统一排序依据,结果不符合规则
- 多表关联会产生重复行,仅加
distinct只能去重,无法解决排序逻辑错误的问题
正确实现代码
适用MySQL、PostgreSQL等支持LEAST函数的数据库,性能最优:
Change .left_joins(:ctasks, :legacy_ctasks) .group(:id) .having("COUNT(ctasks.id) > 0 OR COUNT(legacy_ctasks.id) > 0") .order(Arel.sql("LEAST(MIN(ctasks.start_time), MIN(legacy_ctasks.start_time)) ASC"))
代码逻辑说明
left_joins关联两个子表,拉取所有Change对应的子记录group(:id)按Change主键分组,每个Change仅对应一行聚合结果,自动去重无需额外加distincthaving条件过滤掉没有任何子关联的Change,满足规则2的要求- 排序时先通过
MIN聚合分别拿到当前Change的ctasks最早start_time、legacy_ctasks最早start_time,再通过LEAST取两个时间的最小值作为排序依据,完美匹配规则1的要求
兼容版本(数据库不支持LEAST函数时使用)
Change .left_joins(:ctasks, :legacy_ctasks) .group(:id) .having("COUNT(ctasks.id) > 0 OR COUNT(legacy_ctasks.id) > 0") .order(Arel.sql("MIN(COALESCE(ctasks.start_time, legacy_ctasks.start_time)) ASC"))
内容的提问来源于stack exchange,提问作者prufrock
相关产品推荐
相关产品推荐

