当前位置: 首页 > news >正文

SQL187 每份试卷每月作答数和截止当月的作答总数。

一、题目描述

现有试卷作答记录表 exam_record,包含字段:

  • uid:用户 ID
  • exam_id:试卷 ID
  • start_time:开始作答时间
  • submit_time:交卷时间
  • score:得分

 要求输出:

  1. 每份试卷(exam_id
  2. 每月(格式为 YYYYMM,如 202001
  3. 当月作答次数(month_cnt
  4. 截止当月的累计作答总数cum_exam_cnt

 示例输出:

9001|202001|2|2
9001|202002|1|3
9001|202003|3|6
9001|202005|1|7
9002|202001|1|1
9002|202002|3|4
9002|202003|1|5

📌 解释:
试卷 9001202001 有 2 次作答,累计 = 2
202002 有 1 次,累计 = 2+1=3
202003 有 3 次,累计 = 3+3=6
……以此类推。


 二、正确 SQL 解法

SELECT exam_id,start_month,month_cnt,SUM(month_cnt) OVER (PARTITION BY exam_id ORDER BY start_month) AS cum_exam_cnt
FROM (SELECT exam_id,DATE_FORMAT(start_time, '%Y%m') AS start_month,COUNT(*) AS month_cntFROM exam_recordGROUP BY exam_id, DATE_FORMAT(start_time, '%Y%m')
) t
ORDER BY exam_id, start_month;

 三、分步解析(核心!)

我们把整个查询拆成 4 个步骤,像“做菜”一样一步步来。

第一步:提取“年月”并按月统计

SELECT exam_id,DATE_FORMAT(start_time, '%Y%m') AS start_month,COUNT(*) AS month_cnt
FROM exam_record
GROUP BY exam_id, DATE_FORMAT(start_time, '%Y%m')
📌 做了什么?
  • 使用 DATE_FORMAT(start_time, '%Y%m') 将时间转为 202001 格式。
  • 按 exam_id 和 start_month 分组。
  • 统计每月每卷的作答次数(COUNT(*))。
输出结果(中间表):
exam_idstart_monthmonth_cnt
90012020012
90012020021
90012020033
90012020051
90022020011
90022020023
90022020031

这就是“中间值”!它是后续计算的基础。


 第二步:使用子查询“显式化中间结果”

FROM (-- 上面的查询作为子查询
) t
 为什么需要子查询?
  • 因为 month_cnt 是 GROUP BY 后的聚合结果。
  • 如果直接在 SELECT 中写 SUM(month_cnt)某些数据库不支持引用别名
  • 所以必须用子查询把它“固化”成一个临时表 t,外层才能安全使用。

 类比:你不能一边切菜一边炒,必须先切好(子查询),再炒(外层计算)。


 第三步:窗口函数计算“累计值”

SUM(month_cnt) OVER (PARTITION BY exam_id ORDER BY start_month
)
 拆解窗口函数三要素:
部分作用类比(切蛋糕)
PARTITION BY exam_id把数据按试卷分组把大蛋糕切成几块,每块代表一场试卷
ORDER BY start_month在每块内按时间排序把每块蛋糕的“夹心”按时间排好
SUM(month_cnt)从第一行到当前行累加从第一片开始,逐片切,记录累计大小
累计过程示例(exam_id = 9001):
月份当月次数累计值
20200122
20200212+1=3
20200333+3=6
20200516+1=7

 完全符合“截止当月的作答总数”。


 第四步:排序输出

ORDER BY exam_id, start_month

确保结果按试卷 ID 和时间顺序排列,便于阅读。


 四、常见错误与避坑指南

错误写法问题正确做法
SUM(month_cnt) OVER()全局求和,不分组必须 PARTITION BY exam_id
SUM(COUNT(*)) OVER(...)聚合函数嵌套不合法先 GROUP BY,再用窗口函数
PARTITION BY month_cnt按“次数”分组,无意义应 PARTITION BY exam_id
不用子查询直接引用 month_cnt某些数据库报错用子查询显式构造中间表
ORDER BY start_month 缺失累计顺序不确定必须排序,确保时间顺序

五、核心知识点总结

1. SQL 执行顺序(逻辑)

FROM → WHERE → GROUP BY → SELECT → ORDER BY
  • 窗口函数在 SELECT 阶段执行,在 GROUP BY 之后
  • 所以可以对聚合结果进行窗口计算。

2. 窗口函数公式

FUNCTION(列) OVER (PARTITION BY 分组列   -- 分块ORDER BY 排序列       -- 块内排序ROWS BETWEEN ...      -- 窗口范围(默认从头到当前行)
)

3. 什么时候用子查询?

  • 当你需要对 GROUP BY 后的结果再做复杂计算时。
  • 特别是窗口函数要引用聚合结果时,必须用子查询

 六、举一反三

想要“每场考试的总作答次数”?

SUM(month_cnt) OVER (PARTITION BY exam_id)

→ 每行都显示该试卷的总次数(不累计)。

想要“所有试卷的总作答次数”?

SUM(month_cnt) OVER ()

→ 全局总数,每行都一样。

想要“排名”?

ROW_NUMBER() OVER (PARTITION BY exam_id ORDER BY start_month)

→ 每场考试内,按时间顺序编号。


http://www.dtcms.com/a/528811.html

相关文章:

  • 三河建设局网站做学校网站用什么模版
  • 装修网站建设服务商wordpress 编辑图片无法显示
  • 建设网站要求有哪些营销型网站建设搭建方法
  • jQuery noConflict() 方法详解
  • JavaScript 性能优化系列(六)接口调用优化 - 6.4 错误重试策略:智能重试机制,提高请求成功率
  • 绘画基础知识学习
  • 自己的服务器做网站要备案做网站用到ps么
  • 第 4 篇:SSM 分布式落地:状态持久化与并行状态(含 Redis/MySQL 实战)
  • STM32全栈智慧鱼缸——硬件选型、接线图、软件流程图与完整源码
  • 【11408学习记录】考研数学概率论攻坚:事件的独立性与独立重复试验核心精讲
  • linux下文件操作函数
  • 电商网站建设与维护意味着什么公众号登录怎么退出
  • 专业的营销型网站培训中心wordpress 美化网站
  • 【Java数据结构】——常见力扣题综合
  • 网站长期建设运营计划书江门营销网站建设
  • ProcDump 学习笔记(6.7):监视异常(未处理/首机会/消息过滤/进程终止)
  • C++编程实践——Linux下的CPU控制
  • NTRU 公钥加密系统详解
  • 深入浅出 VGGNet:经典卷积神经网络解析
  • 盐城整站优化柳州做网站去哪家公司好
  • 协程:实战与系统集成(高级篇)
  • 芯片验证基石UVM:高效验证的方法论与挑战
  • 旅游网站开发的作用seo快排技术教程
  • 3DS-GBA-GBC-NDS-switch梦可宝精灵游戏合集 -全汉化游戏
  • VCS Verdi 2023安装
  • R语言~T检验
  • 春季大扫除:清理 Arch Linux 中的垃圾
  • 未在props中声明的属性
  • php网站iis设置同心食品厂网站建设项目任务分解
  • 中国启用WPS格式进行国际交流:政策分析与影响评估