Ruby on Rails:按type_id与created_at筛选has_many关联记录
优化Group与Notes关联查询的两种场景实现
场景1:传入type_id时的筛选逻辑优化
你原来的代码存在两次查询且逻辑有偏差(原逻辑会返回最早日期的所有记录,而非需求中的“不同日期时保留最新记录”),这里提供更简洁且符合需求的实现,同时尽量减少数据库查询次数:
方案1:Ruby层面判断(可读性优先)
if params[:note_type_id].present? type_notes = notes.where(notes_type_id: params[:note_type_id]) # 检查该类型是否存在非今日创建的记录 has_old_entries = type_notes.where.not(created_at: Date.current.all_day).exists? has_old_entries ? type_notes.order(created_at: :desc).first : type_notes end
- 仅两次轻量查询:一次
exists?判断是否有旧记录,一次查询结果集 - 逻辑完全匹配需求:有跨日期记录则返回最新一条,全为今日记录则返回全部
方案2:数据库聚合查询(减少查询次数)
if params[:note_type_id].present? date_info = notes.where(notes_type_id: params[:note_type_id]) .pluck(Arel.sql('MIN(CAST(created_at AS DATE))'), Arel.sql('MAX(CAST(created_at AS DATE))')) .first min_date, max_date = date_info type_notes = notes.where(notes_type_id: params[:note_type_id]) min_date == Date.current && max_date == Date.current ? type_notes : type_notes.order(created_at: :desc).limit(1) end
- 仅两次查询:一次聚合获取该类型的最小/最大日期,一次获取最终结果
- 避免加载多余数据到内存,适合数据量较大的场景
场景2:未传入type_id时的全局筛选逻辑
需求是按类型分组,每组内若存在跨日期记录则仅保留最新一条,全为今日记录则保留该组全部。这里提供两种实现方式:
方案1:Ruby层面处理(新手友好)
else # 按类型分组后逐个处理 grouped_notes = notes.all.group_by(&:notes_type_id) grouped_notes.flat_map do |_, type_notes| # 判断该组是否有非今日记录 has_old_entries = type_notes.any? { |note| note.created_at.to_date != Date.current } has_old_entries ? [type_notes.max_by(&:created_at)] : type_notes end end
- 逻辑直观,容易理解和调试
- 注意:若Notes表数据量极大,
notes.all会加载所有记录到内存,可能影响性能
方案2:数据库窗口函数实现(性能优先,PostgreSQL/MySQL 8.0+适用)
利用窗口函数一次性完成分组、排序和日期判断,适合数据量大的场景:
else notes_with_metadata = notes.select( 'notes.*', # 按类型分组,给每条记录标记组内排序号(最新的为1) 'ROW_NUMBER() OVER (PARTITION BY notes_type_id ORDER BY created_at DESC) AS rn', # 计算每组的最小/最大创建日期 'MIN(CAST(created_at AS DATE)) OVER (PARTITION BY notes_type_id) AS min_date', 'MAX(CAST(created_at AS DATE)) OVER (PARTITION BY notes_type_id) AS max_date' ) # 筛选条件:全为今日记录则保留所有,否则仅保留组内第一条(最新) notes_with_metadata.where( Arel.sql('(min_date = CURRENT_DATE AND max_date = CURRENT_DATE) OR rn = 1') ) end
- 仅一次数据库查询,性能最优
- 需确保数据库支持窗口函数
内容的提问来源于stack exchange,提问作者funtkungus
相关产品推荐
相关产品推荐

