You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

顺序敏感的成功尝试分组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

火山引擎 最新活动