SQL190 0级用户高难度试卷的平均用时和平均得分

一、题目回顾

📄 题目描述

统计每个 level=0 的用户 ,在所有 difficulty='hard' 的试卷中的:

  • ✅ 平均得分(未完成 → 记为 0 分)
  • ✅ 平均用时(未完成 → 记为试卷规定时长 duration
  • avg_score取整(TRUNCATE)
  • avg_time_took保留 1 位小数(ROUND)

📊 示例数据

id uid exam_id start_time submit_time score
1 1001 9001 2020-01-02 09:01:01 2020-01-02 09:21:59 80
2 1001 9001 2021-05-02 10:01:01 NULL NULL
4 1001 9001 2021-06-02 19:01:01 2021-06-02 19:32:00 20

examination_info 表:

exam_id duration difficulty
9001 60 hard

✅ 期望输出

uid avg_score avg_time_took
1001 33 36.7

🧠 二、关键解题思路(四步法)

🔹 第一步:确定数据源与过滤条件

复制代码
FROM user_info ui
JOIN exam_record er ON ui.uid = er.uid
JOIN examination_info ei ON er.exam_id = ei.exam_id
WHERE ui.level = 0 AND ei.difficulty = 'hard'

✅ 只关注 level=0 用户 + 高难度试卷


🔹 第二步:处理"未完成"情况(核心!)

📌 得分处理
  • 未交卷(submit_time IS NULL)或 score IS NULL → 记为 0 分

    CASE
    WHEN er.submit_time IS NULL OR er.score IS NULL THEN 0
    ELSE er.score
    END AS score

⚠️ 不能用 IFNULL(score, 0),因为有些记录 submit_time 是 NULL 但 score 不是(异常数据)


📌 用时处理
  • 未交卷 → 用 ei.duration

  • 异常时间(submit_time < start_time)→ 也用 ei.duration

  • 正常交卷 → 用 TIMESTAMPDIFF(MINUTE, ...)

    CASE
    WHEN er.submit_time IS NULL THEN ei.duration
    WHEN er.submit_time < er.start_time THEN ei.duration
    ELSE TIMESTAMPDIFF(MINUTE, er.start_time, er.submit_time)
    END AS duration_min

💡 重点:用 MINUTE 而不是 /60.0,因为题目要求"20分钟",是向下取整的整数分钟


🔹 第三步:分组聚合

复制代码
GROUP BY result.uid

按用户分组,计算每个用户的平均值


🔹 第四步:格式化输出

复制代码
TRUNCATE(AVG(score), 0)        -- 平均分取整(去小数)
ROUND(AVG(duration_min), 1)    -- 平均用时保留 1 位小数

⚠️ 区别:

  • TRUNCATE(33.9, 0) → 33
  • ROUND(33.5, 0) → 34(四舍五入,不符合"取整"要求)

✅ 三、最终正确 SQL

复制代码
SELECT 
    result.uid,
    TRUNCATE(AVG(score), 0) AS avg_score,
    ROUND(AVG(duration_min), 1) AS avg_time_took
FROM (
    SELECT 
        er.uid,
        -- 未完成或无分 → 0 分
        CASE 
            WHEN er.submit_time IS NULL OR er.score IS NULL THEN 0
            ELSE er.score 
        END AS score,
        -- 未完成或异常时间 → 用 duration;否则用实际分钟数(向下取整)
        CASE 
            WHEN er.submit_time IS NULL THEN ei.duration
            WHEN er.submit_time < er.start_time THEN ei.duration
            ELSE TIMESTAMPDIFF(MINUTE, er.start_time, er.submit_time)
        END AS duration_min
    FROM user_info ui
    JOIN exam_record er ON ui.uid = er.uid
    JOIN examination_info ei ON er.exam_id = ei.exam_id
    WHERE ui.level = 0 AND ei.difficulty = 'hard'
) AS result
GROUP BY result.uid;

🧮 四、结果验证(手动计算)

记录 用时(分钟) 得分
id=1 TIMESTAMPDIFF(MINUTE, '09:01:01', '09:21:59') = 20 80
id=2 duration=60 0
id=4 TIMESTAMPDIFF(MINUTE, '19:01:01', '19:32:00') = 30 20
  • 总用时:20 + 60 + 30 = 110
  • 平均用时:110 / 3 = 36.666... → ROUND(36.666, 1) = 36.7
  • 总得分:80 + 0 + 20 = 100
  • 平均得分:100 / 3 ≈ 33.33 → TRUNCATE(33.33, 0) = 33

✅ 完全匹配期望输出!


🛠 五、常见陷阱与避坑指南

陷阱 错误写法 正确做法
❌ 用 /60.0 算分钟 TIMESTAMPDIFF(SECOND, ...)/60.0 → 20.97 ✅ 用 TIMESTAMPDIFF(MINUTE, ...) → 20
❌ 用 ROUND 取整 ROUND(33.9, 0) → 34 ✅ 用 TRUNCATE(..., 0) → 33
❌ 忽略 submit_time < start_time 直接计算 → 负数 ✅ 用 CASE 判断并替换为 duration
❌ 隐式 JOIN(逗号) FROM a, b, c WHERE ... ✅ 用 JOIN ... ON 显式连接
❌ 外层冗余 JOIN FROM exam_record JOIN (...) ✅ 只在子查询中处理即可

📌 六、高频函数速查表

函数 用途 示例
TIMESTAMPDIFF(MINUTE, start, end) 计算分钟差(向下取整) 20
TIMESTAMPDIFF(SECOND, start, end)/60.0 精确到小数的分钟 20.97
TRUNCATE(x, 0) 去小数取整 33.9 → 33
ROUND(x, 1) 保留 1 位小数 36.66 → 36.7
CASE WHEN ... THEN ... ELSE ... END 条件判断 处理 NULL 和异常
AVG() 求平均值 AVG(score)

🎯 七、总结:解题模板(通用)

复制代码
SELECT 
    result.uid,
    TRUNCATE(AVG(score), 0) AS avg_score,
    ROUND(AVG(duration_min), 1) AS avg_time_took
FROM (
    SELECT 
        er.uid,
        CASE WHEN [未完成条件] THEN 0 ELSE score END AS score,
        CASE WHEN [未完成条件] THEN duration ELSE 实际用时 END AS duration_min
    FROM user_info ui
    JOIN exam_record er ON ...
    JOIN examination_info ei ON ...
    WHERE [业务过滤条件]
) AS result
GROUP BY result.uid;

只需替换 [条件][用时计算] 即可复用!

相关推荐
Mos_x8 小时前
Python爬虫---中国大学MOOC爬取数据(文中有
java·后端
煎蛋学姐8 小时前
SSM基于框架在线电影评论投票系统3gr0f(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·系统开发·ssm 框架·在线电影评论投票系统
꒰ঌ 安卓开发໒꒱8 小时前
Go高并发在企业级项目中的实战应用:数据库访问与GIN+GORM深度实践
数据库·golang·gin
半夏知半秋9 小时前
mongodb的复制集整理
服务器·开发语言·数据库·后端·学习·mongodb
_安晓9 小时前
Rust 中精确大小迭代器(ExactSizeIterator)的深度解析与实践
java·前端·python
程序员柳9 小时前
基于深度学习技术实现染色质开放区域的预测与分析系统源代码+数据库,采用Flask + Vue3 实现前后端分离的植物染色质可及性预测系统
数据库·深度学习·flask
苦学编程的谢9 小时前
Redis_3_Redis介绍+常见命令
数据库·redis·github
Javatutouhouduan9 小时前
记一次redis主从切换导致的数据丢失与陷入只读状态故障
java·redis·设计模式·java面试·高可用·java后端·java程序员
JavaEdge.9 小时前
榨干 CPU 性能:通过绑核将 Redis 尾延迟减半!
数据库·redis·缓存