You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 21:53:19