【LeetCode题库】1280. 学生们参加各科测试的次数 —— 连接查询

我是一名立志把细节说清楚的博主,欢迎【关注】🎉 ~

原创不易, 如果有帮助 ,记得【点赞】【收藏】 哦~ ❥(^_-)~

如有错误、疑惑,欢迎【评论】指正探讨,我会尽可能第一时间回复的,谢谢支持


原文

学生表: Students

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| student_id    | int     |
| student_name  | varchar |
+---------------+---------+

在 SQL 中,主键为 student_id(学生ID)。
该表内的每一行都记录有学校一名学生的信息。

科目表: Subjects

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| subject_name | varchar |
+--------------+---------+

在 SQL 中,主键为 subject_name(科目名称)。
每一行记录学校的一门科目名称。

考试表: Examinations

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| student_id   | int     |
| subject_name | varchar |
+--------------+---------+

这个表可能包含重复数据(换句话说,在 SQL 中,这个表没有主键)。
学生表里的一个学生修读科目表里的每一门科目。
这张考试表的每一行记录就表示学生表里的某个学生参加了一次科目表里某门科目的测试。

查询出每个学生参加每一门科目测试的次数,结果按 student_idsubject_name 排序。

查询结构格式如下所示。

示例 1:

输入:
Students table

+------------+--------------+
| student_id | student_name |
+------------+--------------+
| 1          | Alice        |
| 2          | Bob          |
| 13         | John         |
| 6          | Alex         |
+------------+--------------+

Subjects table:

+--------------+
| subject_name |
+--------------+
| Math         |
| Physics      |
| Programming  |
+--------------+

Examinations table:

+------------+--------------+
| student_id | subject_name |
+------------+--------------+
| 1          | Math         |
| 1          | Physics      |
| 1          | Programming  |
| 2          | Programming  |
| 1          | Physics      |
| 1          | Math         |
| 13         | Math         |
| 13         | Programming  |
| 13         | Physics      |
| 2          | Math         |
| 1          | Math         |
+------------+--------------+

输出:

+------------+--------------+--------------+----------------+
| student_id | student_name | subject_name | attended_exams |
+------------+--------------+--------------+----------------+
| 1          | Alice        | Math         | 3              |
| 1          | Alice        | Physics      | 2              |
| 1          | Alice        | Programming  | 1              |
| 2          | Bob          | Math         | 1              |
| 2          | Bob          | Physics      | 0              |
| 2          | Bob          | Programming  | 1              |
| 6          | Alex         | Math         | 0              |
| 6          | Alex         | Physics      | 0              |
| 6          | Alex         | Programming  | 0              |
| 13         | John         | Math         | 1              |
| 13         | John         | Physics      | 1              |
| 13         | John         | Programming  | 1              |
+------------+--------------+--------------+----------------+

解释:
结果表需包含所有学生和所有科目(即便测试次数为0):
Alice 参加了 3 次数学测试, 2 次物理测试,以及 1 次编程测试;
Bob 参加了 1 次数学测试, 1 次编程测试,没有参加物理测试;
Alex 啥测试都没参加;
John 参加了数学、物理、编程测试各 1 次。


题解

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

解题笔记

本题解题过程,主要有 4 个步骤:

  1. 查询出有关参加考试次数的数据。
  2. 获取 学生名称 与 考试科目 的所有组合。
  3. 通过左连接查询合并数据。
  4. 处理学生没有参加的考试,将参加次数由 NULL 处理为 0。

第一步:我们需要先查询统计处每个学生参加每个科目的考试次数。

SELECT 
    student_id, subject_name, COUNT(*) AS attended_exams
FROM 
    Examinations
GROUP BY 
    student_id, subject_name

结果如下:

+---------------+--------------+----------------+
| student_id	| subject_name | attended_exams |
+---------------+--------------+----------------+
|             1 |         Math |              3 |
|             1 |      Physics |              2 |
|             1 |  Programming |              1 |
|             2 |  Programming |              1 |
|            13 |         Math |              1 |
|            13 |  Programming |              1 |
|            13 |      Physics |              1 |
|             2 |         Math |              1 |
+---------------+--------------+----------------+

第二步:为了获得 (student_idsubject_name) 的所有组合,我们使用交叉联接将表 Student 中的每一行与表 Subject 中的每一行组合在一起,从而得到两个表中的 student_idsubject_name 的所有可能组合。

SELECT 
    *
FROM
    Students s
CROSS JOIN
    Subjects sub

结果如下:

+------------+--------------+--------------+
| student_id | student_name | subject_name |
+------------+--------------+--------------+
|          1 |        Alice |  Programming |
|          1 |        Alice |      Physics |
|          1 |        Alice |         Math |
|          2 |          Bob |  Programming |
|          2 |          Bob |      Physics |
|          2 |          Bob |         Math |
|         13 |         John |  Programming |
|         13 |         John |      Physics |
|         13 |         John |         Math |
|          6 |         Alex |  Programming |
|          6 |         Alex |      Physics |
|          6 |         Alex |         Math |
+------------+--------------+--------------+

第三步:我们将第一步查询的表作为子查询表,命名为表 grouped,并与第二步查询进行左连接,使用 (student_idsubject_name) 对作为标识符来保留所有组合,合并两个表。

第四步:对 grouped.attended_exams 列可能存在 null 值,我们使用 IFNULL() 函数将其替换为0。

最终SQL:

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

我是一名立志把细节说清楚的博主,欢迎【关注】🎉 ~

原创不易, 如果有帮助 ,记得【点赞】【收藏】 哦~ ❥(^_-)~

如有错误、疑惑 ,欢迎【评论】指正探讨,我会尽可能第一时间回复的,谢谢支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我梦Leo

谢谢无敌帅气可爱迷人的你哦 ~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值