【问题标题】:Find the students who attended at least one exam but not the Max nor the Min score查找至少参加过一项考试但未参加最高或最低分数的学生
【发布时间】:2020-12-05 15:31:26
【问题描述】:

我的解决方案通过了第一个测试用例,但在最终提交后得到了错误的答案。我感谢任何愿意指出我的错误的人。谢谢!

问题如下:

表:学生

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

student_id 是该表的主键。 student_name 是学生的姓名。

表格:考试

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| exam_id       | int     |
| student_id    | int     |
| score         | int     |
+---------------+---------+

(exam_id, student_id) 是该表的主键。 拥有 student_id 的学生在 id 为exam_id 的考试中获得分数。

“相当”的学生是指至少参加过一次考试并且既没有获得高分也没有获得低分的学生。

编写 SQL 查询以报告学生(student_id、student_name)在所有考试中“安静”。

不要让从未参加过任何考试的学生退学。返回按 student_id 排序的结果表。

查询结果格式如下例。

学生桌:

+-------------+---------------+
| student_id  | student_name  |
+-------------+---------------+
| 1           | Daniel        |
| 2           | Jade          |
| 3           | Stella        |
| 4           | Jonathan      |
| 5           | Will          |
+-------------+---------------+

考试表:

+------------+--------------+-----------+
| exam_id    | student_id   | score     |
+------------+--------------+-----------+
| 10         |     1        |    70     |
| 10         |     2        |    80     |
| 10         |     3        |    90     |
| 20         |     1        |    80     |
| 30         |     1        |    70     |
| 30         |     3        |    80     |
| 30         |     4        |    90     |
| 40         |     1        |    60     |
| 40         |     2        |    70     |
| 40         |     4        |    80     |
+------------+--------------+-----------+

结果表:

+-------------+---------------+
| student_id  | student_name  |
+-------------+---------------+
| 2           | Jade          |
+-------------+---------------+

问题中的解释:

对于考试 1:学生 1 和 3 分别获得最低和最高分。 对于考试 2:学生 1 持有最高分和最低分。 对于考试 3 和 4:Studnet 1 和 4 分别获得最低和最高分。 学生 2 和 5 从未在任何考试中获得最高或最低的分数。 由于学生 5 没有参加任何考试,因此他被排除在结果之外。 所以,我们只返回学生 2 的信息。

我的答案是创建两张表,一张是列出符合条件的学生,至少有一次考试。 另一种是找到考试表的最大值(分数)和最小值(分数)。 并使用 定位安静学生的 id,然后加入 Student 表查找 这个 student_id 的名字,如下:

-- get eligible student

with eligible_student as
(
select distinct student_id as eligible_id from Exam
    group by 1
    order by 1
),

-- get the high and low score
high_low as
(select student_id, max(score) as high_score, min(score) as low_score
from Exam), 

result as 
 (select eligible_student.eligible_id as student_id
 from eligible_student inner join
 high_low 
 on eligible_student.eligible_id <> high_low.student_id
-- left join Student
-- on eligible_student.eligible_id = Student.student_id 
 group by student_id
 order by student_id
 )
 
 select result.student_id, s.student_name as student_name
 from result left join Student s
 on result.student_id = s.student_id
 order by student_id;


【问题讨论】:

    标签: mysql sql inner-join having-clause


    【解决方案1】:

    我会使用窗口函数和聚合:

    select s.*
    from student s
    inner join (
        select e.*, 
            rank() over(partition by exam_id order by score) as rn_asc,
            rank() over(partition by exam_id order by score desc) as rn_desc
        from exam e
    ) e on e.student_id = s.student_id
    group by s.student_id
    having min(rn_asc) > 1 and min(rn_desc) > 1
    

    子查询通过升序和降序对具有相同考试的记录进行排名,允许平局。然后,我们可以将其与学生表连接起来(这会消除根本没有考试的学生),按学生分组,并过滤​​出两个排名都未达到 1 的学生。

    【讨论】:

    • 感谢您的回复!我可以问 rank() over() 允许平局,但排名中会有差距吗?或者在这里无关紧要,因为我们只关心最大值和最小值。谢谢!
    • @Chen:是的,排名可能有差距,但这里确实没关系,因为我们只看排名最高和最低的。
    【解决方案2】:

    这个查询:

    SELECT *,
         (MIN(score) OVER (PARTITION BY exam_id) = score) +
         (MAX(score) OVER (PARTITION BY exam_id) = score) flag
    FROM exam
    

    当学生的分数既不是考试的最低分数也不是最高分数时,返回一个flag 列,其值为0

    您可以对上述查询的结果进行聚合,以获取所有没有一个flag 且值不同于0 的学生:

    WITH cte AS (
      SELECT *,
             (MIN(score) OVER (PARTITION BY exam_id) = score) +
             (MAX(score) OVER (PARTITION BY exam_id) = score) flag
      FROM exam
    )
    SELECT s.student_id, s.student_name
    FROM student s INNER JOIN cte c
    ON c.student_id = s.student_id
    GROUP BY s.student_id, s.student_name
    HAVING SUM(c.flag) = 0
    

    或者:

    WITH cte AS (
      SELECT student_id
      FROM (
        SELECT *,
             (MIN(score) OVER (PARTITION BY exam_id) = score) +
             (MAX(score) OVER (PARTITION BY exam_id) = score) flag
        FROM exam
      ) t
      GROUP BY student_id
      HAVING SUM(flag) = 0
    )
    SELECT * 
    FROM student
    WHERE student_id IN (SELECT student_id FROM cte)
    

    请参阅demo
    结果:

    > student_id | student_name
    > ---------: | :-----------
    >          2 | Jade        
    

    【讨论】:

    • 嗨@forpas你能解释一下为什么下面是一个标志而不是一个值吗? (MIN(score) OVER (PARTITION BY Exam_id) = score) MAX(score) OVER (PARTITION BY Exam_id) = score)
    • @Ashima:请为此创建一个新问题。这将使每个人都能找到这个问题,回答这个问题,并从中受益。 (我也可以复制 cmets ?)
    • @Luuk :Stackoverflow 不允许我发布问题。我是新来的,所以我没有这个特权。我复制了 cmets 以便从任何人那里得到答案。我的目的是向任何人学习。
    • @Ashima:试试 SELECT 语句,看看它的作用!或者用这个创建一个简单的例子,然后玩它,就像这样DBFIDDLE
    【解决方案3】:

    这部分错了:

    with high_low as
    (select student_id, max(score) as high_score, min(score) as low_score
    from Exam)
    

    因为它输出:

    +------------+------------+-----------+
    | student_id | high_score | low_score |
    +------------+------------+-----------+
    |          1 |         90 |        60 |
    +------------+------------+-----------+
    

    并且 student_id=1 与找到的 high_score 或 low_score 无关。

    在此之后,找到的(但不正确的)student_id 用于选择 cte result

    解决方案:

    with high_low as
    (select max(score) as high_score, min(score) as low_score
    from Exam) 
    select student.student_id, student_name
    from (
      select exam.student_id 
       from exam
       group by exam.student_id
       having max(score) <> (select high_score from high_low)
          and min(score) <> (select low_score from high_low)) x
    inner join student on student.student_id=x.student_id;
    

    或:

    select 
       student.*
    from exam 
    inner join student on student.student_id=exam.student_id
    group by student_id 
    having max(score) not in (select max(score) from exam) 
       and min(score) not in (select min(score) from exam);
    

    【讨论】:

    • 嗨 Luuk,你能解释一下为什么下面是一个标志而不是一个值吗? (MIN(score) OVER (PARTITION BY Exam_id) = score) MAX(score) OVER (PARTITION BY Exam_id) = score)
    • @Ashima:请为此创建一个新问题。这将使每个人都能找到这个问题,回答这个问题,并从中受益。 (顺便说一句:“标志”是@forpas 答案的一部分)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多