Learn How To Write SQL Queries With Examples: #5

Information is the oil of the 21st century, and analytics is the combustion engine.

Peter Sondergaard (Former EVP, Research & Advisory – Gartner)
Question Source: LeetCode
Solution Language: MySQL

This Q&A series will cover data questions from LeetCode and present my solutions to them. Please feel free to comment with your suggestions if you feel that these problems may be solved in a more optimized manner.

Question (LeetCode Question #1412, Level: Hard)

Table: Student

+---------------------+---------+
| Column Name         | Type    |
+---------------------+---------+
| student_id          | int     |
| student_name        | varchar |
+---------------------+---------+
student_id is the primary key for this table.
student_name is the name of the student.

Table: Exam

+---------------+---------+
| Column Name   | Type    |
+---------------+---------+
| exam_id       | int     |
| student_id    | int     |
| score         | int     |
+---------------+---------+
(exam_id, student_id) is the primary key for this table.
Student with student_id got score points in exam with id exam_id.

A “quiet” student is the one who took at least one exam and didn’t score neither the high score nor the low score.

Write an SQL query to report the students (student_id, student_name) being “quiet” in ALL exams.

Don’t return the student who has never taken any exam. Return the result table ordered by student_id.

The query result format is in the following example.

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

Exam table:
+------------+--------------+-----------+
| 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     |
+------------+--------------+-----------+

Result table:
+-------------+---------------+
| student_id  | student_name  |
+-------------+---------------+
| 2           | Jade          |
+-------------+---------------+

For exam 1: Student 1 and 3 hold the lowest and high score respectively.
For exam 2: Student 1 hold both highest and lowest score.
For exam 3 and 4: Studnet 1 and 4 hold the lowest and high score respectively.
Student 2 and 5 have never got the highest or lowest in any of the exam.
Since student 5 is not taking any exam, he is excluded from the result.
So, we only return the information of Student 2.

Solution

WITH cte AS 
(SELECT student_id, 
             score, 
             exam_id,
             (CASE WHEN score < MAX(score) OVER (PARTITION BY exam_id)
             AND score > MIN(score) OVER (PARTITION BY exam_id)
             THEN 'middle'
             ELSE 'highlow'
             END) AS category
FROM Exam
ORDER BY student_id),

cte1 AS (SELECT student_id
         FROM cte
         GROUP BY student_id
         HAVING SUM(CASE WHEN category = 'highlow'
                    THEN 1 ELSE 0
                    END) = 0
)

SELECT cte1.student_id, s.student_name
FROM cte1 JOIN Student s
ON cte1.student_id = s.student_id
ORDER BY cte1.student_id
Alternate Approaches…
WITH cte AS 
     (SELECT student_id, score, exam_id,
            max(score) OVER (PARTITION BY exam_id) AS maxscore,
            min(score) OVER (PARTITION BY exam_id) AS minscore
      FROM Exam),
      cte1 AS 
            (SELECT student_id
                  FROM cte
                  WHERE score = maxscore OR score = minscore
             )
SELECT DISTINCT Exam.student_id, Student.student_name
      FROM Exam JOIN Student
      ON Exam.student_id = Student.student_id
WHERE Exam.student_id NOT IN (SELECT student_id FROM cte1)
      ORDER BY Exam.student_id
WITH cte AS (SELECT student_id,
             rank() OVER (PARTITION BY exam_id ORDER BY score DESC) 
             AS gethighest,
             rank() OVER (PARTITION BY exam_id ORDER BY score ASC) 
             AS getlowest
             FROM Exam),
     cte1 AS (SELECT DISTINCT student_id,
              SUM(CASE WHEN gethighest = 1 
                            OR getlowest = 1 
                       THEN 1 
                       ELSE 0 END) 
                  OVER (PARTITION BY student_id ORDER BY student_id)
                  AS numofhighlow
              FROM cte)
SELECT cte1.student_id, student_name
      FROM cte1 JOIN Student
      ON cte1.student_id = Student.student_id
      WHERE cte1.numofhighlow = 0