SQL 189 统计有未完成状态的试卷的未完成数和未完成率

题目描述

现有表 exam_record,记录用户作答试卷的信息:

字段名 含义
uid 用户 ID
exam_id 试卷 ID
start_time 开始作答时间
submit_time 交卷时间(NULL 表示未完成)
score 得分

要求:

统计有未完成状态的试卷的:

  • incomplete_cnt:未完成数量(即 submit_time 为 NULL 的记录数)
  • incomplete_rate:未完成率(保留 3 位小数)

✅ 只输出未完成率 > 0 的试卷。


解题思路

Step 1:理解"未完成"的定义

  • 一条记录只要 submit_time IS NULL,就表示未完成。
  • 注意:start_time 通常非空(表示已开始作答),所以每条记录都算一次"作答尝试"。

Step 2:核心统计逻辑

  • 每个 exam_id 分组后:
    • 总作答次数 = COUNT(*)
    • 已完成次数 = COUNT(submit_time)(自动忽略 NULL)
    • 未完成次数 = COUNT(*) - COUNT(submit_time)
    • 未完成率 = (未完成次数) / (总次数)

Step 3:过滤条件

  • 外层使用 WHERE incomplete_rate > 0,只保留有未完成记录的试卷。

正确代码

复制代码
SELECT
    exam_id,
    incomplete_cnt,
    incomplete_rate
FROM
    (
        SELECT
            exam_id,
            COUNT(*) - COUNT(submit_time) AS incomplete_cnt,
            ROUND((COUNT(*) - COUNT(submit_time)) * 1.0 / COUNT(*), 3) AS incomplete_rate
        FROM
            exam_record
        GROUP BY
            exam_id
    ) AS exam_stats
WHERE
    incomplete_rate > 0;

代码解析(逐行详解)

行号 代码片段 说明
1-3 SELECT exam_id, incomplete_cnt, incomplete_rate 选择最终输出字段
4 FROM (...) AS exam_stats 使用子查询封装聚合结果,提高可读性
6 COUNT(*) - COUNT(submit_time) 计算未完成数量: • COUNT(*):总记录数 • COUNT(submit_time):非 NULL 的交卷数(即已完成)
7 ROUND((...) * 1.0 / COUNT(*), 3) 计算未完成率: • * 1.0 强制转为浮点数,避免整数除法(如 1/3=0) • ROUND(..., 3) 保留三位小数
8 FROM exam_record 数据源
9 GROUP BY exam_id 按试卷分组,进行聚合统计
12-14 WHERE incomplete_rate > 0 过滤掉"全部完成"的试卷(即未完成率为 0 的)

易错点与注意事项

问题 说明 如何避免
❌ WHERE 中使用聚合函数 COUNT() 不能在 WHERE 中直接使用 使用 HAVING 或子查询
❌ 整数除法导致结果为 0 (3-2)/3 = 0(整数除法) 加 * 1.0 或 CAST(... AS FLOAT)
❌ 错误使用 COUNT(start_time) 本题中 start_time 总是非空,可用 COUNT(*) 更简洁 统一使用 COUNT(*) 作为总次数
❌ 忘记 GROUP BY 聚合查询必须分组 记住:有聚合函数 + 非聚合字段 → 必须 GROUP BY
❌ 未过滤 incomplete_rate = 0 题目要求"有未完成状态" 外层加 WHERE incomplete_rate > 0

💡 关键知识点总结

知识点 说明
COUNT(column) 只统计 column IS NOT NULL 的行
COUNT(*) 统计所有行,包括 NULL 值
聚合函数不能在 WHERE 必须用 HAVING 或子查询
浮点除法陷阱 整数除法会截断小数,需转浮点
ROUND(x, 3) 保留 3 位小数
子查询封装 先聚合,再过滤,结构更清晰
复制代码
exam_id   incomplete_cnt   incomplete_rate
9001      1                0.333

复习口诀(助记)

"先分组,再聚合;
总数减完成,得未完数;
乘1.0防整除,ROUND保留三;
外层过滤零,子查结构佳。"


终于不是窗口函数了,有种解放了的感觉

相关推荐
草莓熊Lotso2 小时前
【C++ 基于微服务的即时通讯系统】etcd + brpc 从安装到服务注册发现全链路实战
网络·数据库·c++·redis·微服务·etcd
Lethehong7 小时前
Oracle 加个字段,目标库就报错?KFS 用“先拆后组“打通异构 DDL
数据库·oracle
Web极客码8 小时前
锁定真正的磁盘卡顿:我用 OpenCode 配合 eBPF 诊断 NVMe 队列深度延迟与 I/O 写放大
数据库·oracle
cspttty8 小时前
2027用户研究求职准备:应用统计学如何补齐研究设计与业务表达
大数据·数据库
roman_日积跬步-终至千里9 小时前
【数据Agent论文·查询与语义|05】Spider:为什么换一个数据库,模型就不会查了?
数据库·数据分析
猫咪很可爱10 小时前
Oracle RAC 启动失败排查:私网通信与内核参数优化实战
运维·数据库·oracle
XLYcmy10 小时前
《数字经济赋能城市低碳治理的路径研究检索报告》审阅与修改指导意见——基于完整检索报告体例的完整性诊断、学术规范核查与方法论纠偏 上
数据库·ai·chatgpt·文心一言·检索·知网·豆包
泡海椒10 小时前
JQuick-SQL 快速开始:注册内存表并执行查询
数据库·windows·sql
ss27311 小时前
PG实战笔记 | 09 PostgreSQL psql客户端基本操作
数据库·笔记·postgresql
电商API_1800790524711 小时前
反向海淘多币种结算:汇率该在哪个时点被冻结
java·开发语言·数据库·跨境电商·1688 api