[英]mysql - find all students of a class if at least one student attended
[英]Find the students who attended at least one exam but not the Max nor the Min score
我的解決方案通過了第一個測試用例,但在最終提交后得到了錯誤的答案。 我感謝任何願意指出我的錯誤的人。 謝謝!
問題如下:
表:學生
+---------------------+---------+
| 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 表連接,找到這個學生的名字,如下所示:
-- 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;
我會使用 window 函數和聚合:
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
的學生。
這個查詢:
SELECT *,
(MIN(score) OVER (PARTITION BY exam_id) = score) +
(MAX(score) OVER (PARTITION BY exam_id) = score) flag
FROM exam
當學生的分數既不是考試的最小值也不是最大值時,返回一個值為0
的flag
列。
您可以匯總上述查詢的結果,以獲取所有沒有一個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)
請參閱演示。
結果:
> student_id | student_name
> ---------: | :-----------
> 2 | Jade
這部分是錯誤的:
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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.