MySQL单查询实现分组统计与前3日成功率均值计算
解决方案:添加前3日成功率均值列
要实现你需要的第5项需求,我们可以利用MySQL的窗口函数来计算滚动平均值,同时控制仅从第4条记录开始展示该均值。下面是改造后的完整查询语句,以及详细的逻辑说明:
完整查询代码
SELECT day, fails, success, total, success_percent, CASE WHEN row_num >= 4 THEN AVG(success_percent) OVER (ORDER BY day ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING) ELSE NULL END AS prev_3d_avg_success_percent FROM ( SELECT day, fails, success, fails + success AS total, ROUND((success * 100 / (success + fails)), 2) AS success_percent, -- 保留两位小数优化显示 ROW_NUMBER() OVER (ORDER BY day) AS row_num FROM ( -- 你的原始统计子查询,负责计算每日基础数据 SELECT day, SUM(IF(status='FAIL', 1, 0)) AS fails, SUM(IF(status='SUCCESS', 1, 0)) AS success FROM statuses GROUP BY day ) AS inner_query ) AS outer_query;
逻辑拆解
我们逐步来看这段代码的作用:
- 基础统计层(inner_query):这部分就是你原本实现的逻辑,按日期分组,统计每日的失败数、成功数。
- 中间计算层:
- 计算每日总状态数
total和成功率success_percent,这里用ROUND()函数把成功率保留两位小数,让结果更整洁; - 用
ROW_NUMBER() OVER (ORDER BY day)生成按日期排序的行号,方便后续判断是否是第4条及以后的记录。
- 计算每日总状态数
- 最终结果层:
- 使用
AVG(success_percent) OVER (...)窗口函数,指定窗口范围为ROWS BETWEEN 3 PRECEDING AND 1 PRECEDING——意思是取当前行往前数第3行到第1行的数据,正好对应前3天的成功率; - 通过
CASE语句过滤:只有当行号≥4时,才展示前3天的平均成功率,前3条记录则显示NULL(你可以根据需求改为0或其他默认值)。
- 使用
效果验证
对应你提供的测试数据,最终结果会是这样:
- 2018-01-01、01-02、01-03这三天的
prev_3d_avg_success_percent为NULL; - 2018-01-04的均值是2018-01-01、01-02、01-03三天成功率的平均值;
- 2018-01-05的均值是2018-01-02、01-03、01-04三天成功率的平均值。
内容的提问来源于stack exchange,提问作者Pavel




