目录

题目描述

1280. 学生们参加各科测试的次数

题意分析

手上有三张表:学生表只有编号和姓名,科目表只有科目名,考试表是一条条「谁考了哪科」的流水,同一个人同一科可以出现多次。要交出的东西是一张固定形状的报表:每个学生对每门科目各占一行,附带这个人考这门课的次数。

关键信号藏在「即使一次都没考也要出现」这句话里。结果的行数由学生表和科目表共同决定,和考试流水里出现过哪些组合无关;考试流水只负责往这张已经定好形状的报表里填数字。这两件事必须分开做,否则很容易写成「流水里有什么就输出什么」。

边界主要有三处:某个学生一门课都没考过,他仍要出现三行且全为 0;某门科目谁都没考过,这一列也不能消失;同一个学生同一科的多条流水要累加而不是去重。输出还要求先按学生编号、再按科目名升序。

解法:笛卡尔积 + 左连接聚合

核心思路

结果必须包含每个「学生 × 科目」组合,即使该组合在考试流水中从未出现。因此不能从 Examinations 出发聚合;不存在的组合无法凭空补回。

先用 Students CROSS JOIN Subjects 建立完整结果骨架,再把 Examinations 左连接进来:

  • CROSS JOIN 保证每个学生都有每门科目;
  • LEFT JOIN 保证没有考试记录的组合仍保留;
  • COUNT(e.subject_name) 只统计右表的非空匹配,未参加时结果自然为 0。

连接条件必须同时包含学生编号和科目名,并写在 ON 中。若把右表条件放进 WHERE,补出的空行会被过滤,左连接就退化成内连接。

解题步骤

  1. StudentsSubjects 做笛卡尔积,得到报表的全部行。
  2. 按学生编号、科目名左连接考试流水。
  3. 按学生编号、姓名、科目名分组,用 COUNT(e.subject_name) 统计匹配记录数。
  4. 按学生编号、科目名升序排列结果。

若有 4 名学生、3 门科目,骨架固定为 12 行。某个组合没有考试记录时,左连接仍保留一行,只是 e.subject_nameNULL,所以 COUNT(e.subject_name) 返回 0。

代码实现

SELECT
    s.student_id,
    s.student_name,
    sub.subject_name,
    COUNT(e.subject_name) AS attended_exams
FROM Students AS s
CROSS JOIN Subjects AS sub
LEFT JOIN Examinations AS e
    ON e.student_id = s.student_id
   AND e.subject_name = sub.subject_name
GROUP BY s.student_id, s.student_name, sub.subject_name
ORDER BY s.student_id, sub.subject_name;

复杂度分析

  • 设学生数为 $S$、科目数为 $C$、考试记录数为 $E$。结果本身就有 $SC$ 行,因此至少需要 $O(SC)$ 的处理与输出成本。
  • 在哈希连接或连接列有合适索引时,连接与聚合通常可在 $O(SC + E)$ 量级完成;最终排序约为 $O(SC log(SC))$。具体执行代价取决于数据库优化器和索引。
  • 中间结果与分组结构需要 $O(SC + E)$ 量级空间,具体同样由执行计划决定。

关键点总结

  • 「零次也要展示」意味着先构造维度组合,再左连接事实表。
  • COUNT(列) 忽略 NULL,而 COUNT(*) 会把左连接补出的空行也计为 1。
  • 外连接右表的匹配条件应留在 ON 中;放进 WHERE 会删除未匹配行。
  • 分组粒度必须与结果粒度一致,即一个学生的一门科目一行。
  • 面试追问若考试表很大,可先按 student_id, subject_name 预聚合,再与骨架连接,减少连接阶段的行膨胀。

易错点总结

  • 从考试表直接分组:从未参加考试的学生或科目组合不会出现。
  • 使用内连接:零次记录被整体丢弃。
  • 写成 COUNT(*):无匹配组合也会被计为 1。
  • 只按学生编号连接:同一学生不同科目的记录会混在一起。
  • 把右表过滤条件写入 WHERE:左连接退化成内连接。
  • 遗漏科目名排序:同一学生内的输出顺序不满足题目要求。

相似题目

题目 难度 考察点
175. 组合两个表 简单 单次左连接保留主表全部行,无需笛卡尔积骨架
183. 从不订购的客户 简单 反向利用左连接后的 NULL 做差集筛选
570. 至少有5名直接下属的经理 中等 自连接后用 HAVING 对聚合结果加阈值
1341. 电影评分 中等 多表连接后分组取极值并用 UNION 合并两问