SQL多表查询:获取每个主表记录对应的从表最新单条数据
解决方案
你遇到的是典型的「分组取最新/第一条」场景,原写法的问题在于用了全局LIMIT,只会对整个结果集做条数截断,无法按关联的外键分组分别取数,以下是可直接使用的实现方式:
1. 主流数据库通用写法(支持MySQL 8.0+、PostgreSQL、SQL Server、SQLite 3.25+等所有支持窗口函数的版本)
用窗口函数ROW_NUMBER()按外键分区、按时间倒序排序,筛选每个分区序号为1的记录即可,性能远好于子查询关联写法:
SELECT t2.* FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY fk ORDER BY time DESC) AS row_num FROM tbl2 WHERE fk IN (SELECT id FROM tbl1 WHERE some_criteria) ) t2 WHERE t2.row_num = 1;
- 注意:如果同一外键
fk下存在多条time完全相同的记录,上述语句会随机返回其中一条;如果需要把同时间的所有记录都返回,把ROW_NUMBER()替换为RANK()即可。
如果需要保留tbl1中没有对应tbl2匹配记录的条目(即tbl2关联0条记录时也返回tbl1内容,对应tbl2字段显示NULL),使用左连接写法:
SELECT t1.*, t2.* FROM (SELECT * FROM tbl1 WHERE some_criteria) t1 LEFT JOIN ( SELECT *, ROW_NUMBER() OVER (PARTITION BY fk ORDER BY time DESC) AS row_num FROM tbl2 ) t2 ON t1.id = t2.fk AND t2.row_num = 1;
2. 老版本数据库兼容写法(不支持窗口函数,如MySQL 5.x)
先按外键分组取出每个外键对应的最大时间,再关联回tbl2取出对应记录的全部字段:
SELECT t2.* FROM tbl2 t2 INNER JOIN ( SELECT fk, MAX(time) AS latest_time FROM tbl2 WHERE fk IN (SELECT id FROM tbl1 WHERE some_criteria) GROUP BY fk ) t2_latest ON t2.fk = t2_latest.fk AND t2.time = t2_latest.latest_time;
- 注意:该写法如果遇到同一外键下多条记录时间等于最大时间的情况,会把这些记录全部返回。
原写法问题说明
- 带
LIMIT 1的写法:全局限制结果集仅返回1条,自然无法覆盖所有符合条件的tbl1记录 - 仅做时间倒序的写法:没有按外键分组做条数截断,会返回所有匹配的tbl2记录,无法实现单条tbl1对应单条最新tbl2记录的要求
内容的提问来源于stack exchange,提问作者Belkin
相关产品推荐
相关产品推荐

