Rails 5复杂左外自连接实现:获取指定时段用户列表最新事件
获取指定时间段内每个用户对应每个列表的最新事件(Rails 5实现)
嘿,我来帮你搞定这个查询需求!根据你的描述,我们需要在指定时间范围内,为每个user_id和list_id的组合拿到最新的那条事件记录。下面给你两种实用的Rails 5实现方案,按需选择就行。
方案一:窗口函数(推荐,高效处理大数据)
窗口函数是处理这类“分组取最新”场景的最优解,尤其是数据量较大时性能优势明显。我们用ROW_NUMBER()给每个用户-列表组内的记录按创建时间倒序编号,最新的那条编号为1,最后筛选出编号为1的记录即可。
通用查询(所有列表)
假设你已经定义了start_time和end_time作为时间区间:
start_time = Time.parse("14:00") end_time = Time.parse("15:00") # 子查询:给每个(user_id, list_id)组的记录编号 ranked_events = ListEvent .where(created_at: start_time..end_time) .select( "list_events.*", "ROW_NUMBER() OVER (PARTITION BY user_id, list_id ORDER BY created_at DESC) AS row_num" ) # 外层查询:筛选出每组的第一条(最新)记录 latest_events = ListEvent .from(ranked_events, :list_events) .where(row_num: 1)
关联List模型的查询(指定单个列表)
如果是从某个具体列表出发(比如你的示例中列表1),可以简化分组条件(已经限定了list_id):
target_list = List.find(1) ranked_events = target_list.events .where(created_at: start_time..end_time) .select( "events.*", "ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS row_num" ) latest_user_events = target_list.events .from(ranked_events, :events) .where(row_num: 1)
方案二:子查询关联(兼容旧数据库)
如果你的数据库不支持窗口函数(比如老版本MySQL),可以用子查询先获取每个用户-列表组的最新创建时间,再关联原表拿到对应记录:
# 子查询:获取每个(user_id, list_id)组的最大created_at max_event_times = ListEvent .where(created_at: start_time..end_time) .group(:user_id, :list_id) .select(:user_id, :list_id, "MAX(created_at) AS max_created_at") # 关联原表,匹配用户、列表和最新时间 latest_events = ListEvent .joins( "INNER JOIN (#{max_event_times.to_sql}) AS max_times ON list_events.user_id = max_times.user_id AND list_events.list_id = max_times.list_id AND list_events.created_at = max_times.max_created_at" ) .where(created_at: start_time..end_time)
⚠️ 注意:如果同一个用户-列表组在同一时间有多个事件(created_at完全相同),这个方案会返回所有符合的记录;而窗口函数方案可以通过调整ORDER BY(比如ORDER BY created_at DESC, id DESC)只取其中一条。
结果验证
针对你给出的示例数据,执行上述查询后,会正确返回列表1在14:00-15:00内的用户最新事件:
| id | user_id | list_id | event | created_at |
|---|---|---|---|---|
| 4 | 3 | 1 | sub | 14:03 |
| 5 | 4 | 1 | sub | 14:04 |
| 6 | 1 | 1 | unsub | 14:05 |
内容的提问来源于stack exchange,提问作者Schwern
相关产品推荐
相关产品推荐

