MySQL使用JOIN关联user、activity a、b表时如何在行尾显示各表最新数据
MySQL实现用户关联双活动表最新记录的查询方案
先提前说明:所有写法基于通用表结构约定——user表以user_id作为主键关联两张活动表,activity_a、activity_b都通过user_id关联对应用户,且有create_time(记录创建时间)、自增主键record_id用于判断哪条记录是最新的。如果你的表字段名有差异,替换成实际字段即可。
核心避坑提醒
直接把user表和两个活动表做普通关联会出现行数膨胀的问题:一个用户在活动表有多条历史记录时,关联后会生成多倍的结果行,根本实现不了“每个用户一行、末尾带最新活动记录”的效果。所以必须先从每个活动表中把每个用户的最新一条记录单独筛选出来,再和全量user表做左关联。
不同MySQL版本的SQL写法
1. MySQL 8.0及以上版本(支持窗口函数,写法最简洁)
用窗口函数给每个用户的所有活动记录按时间倒序排个序,排第一的就是最新的那条,直接取出来就行:
SELECT u.*, a_latest.需要从a表取的字段1, a_latest.需要从a表取的字段2, b_latest.需要从b表取的字段1, b_latest.需要从b表取的字段2 FROM `user` u -- 关联a表的用户最新记录子集 LEFT JOIN ( SELECT * FROM ( SELECT *, -- 按用户分组,活动时间倒序排序,同时间下取id更大的,保证结果稳定 ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY create_time DESC, record_id DESC ) AS row_rank FROM activity_a ) a_ranked -- 只留每个用户排名第一的,也就是最新的那条 WHERE row_rank = 1 ) a_latest ON u.user_id = a_latest.user_id -- 关联b表的用户最新记录子集,逻辑和a表完全一致 LEFT JOIN ( SELECT * FROM ( SELECT *, ROW_NUMBER() OVER ( PARTITION BY user_id ORDER BY create_time DESC, record_id DESC ) AS row_rank FROM activity_b ) b_ranked WHERE row_rank = 1 ) b_latest ON u.user_id = b_latest.user_id;
2. MySQL 5.7及以下版本(不支持窗口函数)
用NOT EXISTS的逻辑筛选最新记录:对活动表的某条记录,如果同一个用户下没有比它时间更新、id更大的记录,那这条就是最新的:
SELECT u.*, a_latest.需要从a表取的字段1, a_latest.需要从a表取的字段2, b_latest.需要从b表取的字段1, b_latest.需要从b表取的字段2 FROM `user` u LEFT JOIN activity_a a_latest ON u.user_id = a_latest.user_id -- 核心筛选逻辑:不存在比当前a表记录更新的同用户记录 AND NOT EXISTS ( SELECT 1 FROM activity_a a_next WHERE a_next.user_id = a_latest.user_id AND ( a_next.create_time > a_latest.create_time OR (a_next.create_time = a_latest.create_time AND a_next.record_id > a_latest.record_id) ) ) LEFT JOIN activity_b b_latest ON u.user_id = b_latest.user_id AND NOT EXISTS ( SELECT 1 FROM activity_b b_next WHERE b_next.user_id = b_latest.user_id AND ( b_next.create_time > b_latest.create_time OR (b_next.create_time = b_latest.create_time AND b_next.record_id > b_latest.record_id) ) );
实操调整提示
- 关联一定要用LEFT JOIN,如果换成INNER JOIN,没有产生过活动记录的用户会被直接过滤,不符合“取user表全部数据”的需求,这类用户在结果里的活动字段位置会自动填充为
NULL - 判断“最新”的排序规则可以按业务调整:如果是按活动参与时间、记录更新时间判断新旧,把SQL里
ORDER BY后面的字段换成对应业务字段即可 - 活动表数据量大的场景,记得在
user_id, create_time字段上建联合索引,避免全表扫描拖慢查询 - 写查询时尽量明确写出需要从活动表取的字段,不要直接用
*,避免两个活动表存在同名字段,导致结果列冲突
内容的提问来源于stack exchange,提问作者Arif hidayah
相关产品推荐
相关产品推荐

