顺序敏感的成功尝试分组SQL查询:统计跳高运动员各次试跳成功率
Hey,这个需求其实是典型的按序列位置做聚合分析,我来分享下最优的实现方案,不管是可读性还是执行效率都拉满的那种👇
首先,我们得先理清核心逻辑:要计算每个“第x次试跳”的成功概率,第一步必须给每个运动员的所有试跳按顺序编号(毕竟attempt_id可能只是主键,不一定能直接代表试跳的先后顺序?当然如果你的业务里attempt_id就是严格按试跳生成的,那刚好能用)。
最优实现步骤
1. 给每个运动员的试跳生成“试跳次数”编号
用窗口函数ROW_NUMBER()是最优雅高效的方式,它能给每个运动员的试跳单独排序,生成第1次、第2次...的编号:
WITH athlete_attempts AS ( SELECT athlete, result, -- 按运动员分组,按试跳ID(或时间)排序,生成试跳次数 ROW_NUMBER() OVER (PARTITION BY athlete ORDER BY attempt_id) AS attempt_number FROM Jumper )
注意:如果你的表有
attempt_time(试跳时间)字段,把ORDER BY attempt_id换成ORDER BY attempt_time会更准确,毕竟有时候ID可能因为业务原因不是严格时序的。
2. 按试跳次数分组统计
基于上面的CTE,我们直接分组聚合就能拿到需要的所有指标,还能处理除以0的边界情况:
SELECT attempt_number AS 试跳次数, -- 统计成功次数,这里用CASE兼容大部分数据库,PostgreSQL还能用FILTER更简洁 SUM(CASE WHEN result = TRUE THEN 1 ELSE 0 END) AS 成功试跳次数, COUNT(*) AS 总试跳次数, -- 计算成功概率,保留2位小数,避免除以0报错 ROUND( CASE WHEN COUNT(*) = 0 THEN 0 ELSE SUM(CASE WHEN result = TRUE THEN 1 ELSE 0 END)::FLOAT / COUNT(*) END, 2 ) AS 成功概率 FROM athlete_attempts GROUP BY attempt_number ORDER BY attempt_number;
如果是PostgreSQL用户,成功次数的统计可以简化成:
SUM(1) FILTER (WHERE result = TRUE) AS 成功试跳次数
为什么这是最优方案?
- 效率高:窗口函数是数据库原生优化的,时间复杂度是O(n log n),比用自关联、子查询生成试跳次数的方式快得多,数据量大的时候差距特别明显。
- 可读性强:CTE把逻辑拆成两步,谁看都能懂,后期维护成本低。
- 兼容性好:支持所有现代关系型数据库(MySQL 8+、PostgreSQL、SQL Server、Oracle等),老版本数据库如果不支持窗口函数才需要退而求其次用变量,但现在基本没人用那么老的版本了。
内容的提问来源于stack exchange,提问作者bkramer




