PostgreSQL查询如何标记各场次比赛最快成绩记录
实现方法
不需要为每个场次编写独立子查询,有更简洁的标准SQL实现方案,根据使用的数据库版本选对应写法即可。
方案1:窗口函数写法(推荐,兼容绝大多数主流数据库新版本)
用MIN() OVER()窗口函数可以在保留所有选手明细行的同时,直接计算出对应赛事下每个场次的全局最快用时,不需要额外构建临时表或做关联,逻辑最简洁:
SELECT r.id, r.race1, -- 对比当前选手race1成绩和该场全局最小值,返回布尔值 r.race1 = MIN(r.race1) OVER(PARTITION BY r.race_id) AS race1_fastest, r.race2, r.race2 = MIN(r.race2) OVER(PARTITION BY r.race_id) AS race2_fastest, r.race3, r.race3 = MIN(r.race3) OVER(PARTITION BY r.race_id) AS race3_fastest FROM racer r WHERE r.race_id = 1;
注意:如果使用Oracle等没有原生布尔类型的数据库,可以用CASE表达式包装返回1/0或自定义标识值,写法通用适配所有支持窗口函数的数据库:
CASE WHEN r.race1 = MIN(r.race1) OVER(PARTITION BY r.race_id) THEN 1 ELSE 0 END AS race1_fastest
这个方案适配MySQL 8.0+、PostgreSQL、SQL Server、SQLite 3.25+等绝大多数主流数据库的现行版本。
方案2:单次聚合子查询写法(兼容不支持窗口函数的旧版本数据库)
如果你用的是不支持窗口函数的旧版本(比如MySQL 5.x),也不需要写三个独立子查询,只需要一次聚合算出三个场次的最快值,做一次关联即可,性能远高于多个独立子查询:
SELECT r.id, r.race1, r.race1 = min_score.min1 AS race1_fastest, r.race2, r.race2 = min_score.min2 AS race2_fastest, r.race3, r.race3 = min_score.min3 AS race3_fastest FROM racer r, ( SELECT MIN(race1) AS min1, MIN(race2) AS min2, MIN(race3) AS min3 FROM racer WHERE race_id = 1 ) min_score WHERE r.race_id = 1;
问题说明
之前尝试用min()加CASE、WITH子查询没成功,核心问题是普通聚合函数配合GROUP BY会将多行结果压缩为单行聚合结果,无法返回所有选手的明细数据;窗口函数的OVER子句就是专门为这种「保留明细行同时计算全局/分组聚合值」的场景设计的,不需要额外拆分逻辑。当前场景只有50条左右的选手数据,两种写法的性能差异可以忽略。
内容的提问来源于stack exchange,提问作者user081608
相关产品推荐
相关产品推荐

