MySQL刷题:学生们参加各科测试的次数

1. 问题描述

给出三张表:

  1. 学生表: Students
    ±--------------±--------+
    | Column Name | Type |
    ±--------------±--------+
    | student_id | int |
    | student_name | varchar |
    ±--------------±--------+
    在 SQL 中,主键为 student_id(学生ID)。该表内的每一行都记录有学校一名学生的信息。
  2. 科目表: Subjects
    ±-------------±--------+
    | Column Name | Type |
    ±-------------±--------+
    | subject_name | varchar |
    ±-------------±--------+
    在 SQL 中,主键为 subject_name(科目名称)。
    每一行记录学校的一门科目名称。
  3. ±-------------±--------+
    | Column Name | Type |
    ±-------------±--------+
    | student_id | int |
    | subject_name | varchar |
    ±-------------±--------+
    这个表可能包含重复数据(换句话说,在 SQL 中,这个表没有主键)。学生表里的一个学生修读科目表里的每一门科目。这张考试表的每一行记录就表示学生表里的某个学生参加了一次科目表里某门科目的测试。
    实例:
    输入:
  4. Students table:
    ±-----------±-------------+
    | student_id | student_name |
    ±-----------±-------------+
    | 1 | Alice |
    | 2 | Bob |
    | 13 | John |
    | 6 | Alex |
    ±-----------±-------------+
  5. Subjects table:
    ±-------------+
    | subject_name |
    ±-------------+
    | Math |
    | Physics |
    | Programming |
    ±-------------+
  6. 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 次。

2. 问题分析

一拿到这个问题,第一反应肯定是三张表的连结,并且通过 student_id 和 subject_name 进行连结,所以

select st.student_id,st.student_name,s.subject_name,count(ex.subject_name) as attended_exams
from examinations as ex left outer join students as st on ex.student_id =st.student_id 
left outer join subjects as s on ex.subject_name=s.subject_name 
group by st.student_id,st.student_name,s.subject_name
order by st.student_id,s.subject_name;

但是这样的显示结果确实这样的:
在这里插入图片描述
但其实最终的目标输出是这样的:
在这里插入图片描述
不难看出,这两张表之间的差异在于存在有的学生并没有参加某些科目的考试,比如 Bob 没有参加物理考试,任何表中都没有这一行,那无论怎么连结,也不可能出现 Bob 参加了零场物理考试,解决的办法要用到 交叉连结 我们需要把所有的学生对应所有的科目,这样的思路就是先对学生表和科目表交叉连结:需要注意的是cross join 并没有连结条件,然后和 考试表外连结:

select st.student_id,st.student_name,su.subject_name
from students as st cross join subjects as su
left outer join examinations as ex 
on su.subject_name=ex.subject_name and ex.student_id =st.student_id
order by st.student_id,su.subject_name;

在这里插入图片描述
下面再实现分组计数的功能:

select st.student_id,st.student_name,su.subject_name,count(ex.student_id) 
from students as st cross join subjects as su
left outer join examinations as ex 
on su.subject_name=ex.subject_name and ex.student_id =st.student_id
group by st.student_id ,st.student_name ,su.subject_name
order by st.student_id,su.subject_name;

在这里插入图片描述
达到了要求,注意,再最后进行 count()计数的时候,一定是对 考试表进行计数的,而不是科目表,因为考试表不是主表,有的单元格是 null

select st.student_id,st.student_name,su.subject_name,ex.student_id ,ex.subject_name 
from students as st cross join subjects as su
left outer join examinations as ex 
on su.subject_name=ex.subject_name and ex.student_id =st.student_id
-- group by st.student_id ,st.student_name ,su.subject_name
order by st.student_id,su.subject_name;

我们看一下未分组之前的
在这里插入图片描述
注意分组的形式,就知道肯定是对 考试表的 subject_name 或者 id 进行计数。
最后看一下完整版的:

select st.student_id,st.student_name,su.subject_name,count(ex.student_id)as attended_exams 
from students as st cross join subjects as su
left outer join examinations as ex 
on su.subject_name=ex.subject_name and ex.student_id =st.student_id
group by st.student_id ,st.student_name ,su.subject_name
order by st.student_id,su.subject_name;
  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

素梦秋影

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值