简体   繁体   中英

How to use join and order_by with multiple records in SQL/SQLalchemy?

I have 2 tables looks like this:

student_info:

| student_id | major |
|------------|-------|
| 1001       | CS    |
| 1002       | CS    |
| 1003       | CS    |
| 1004       | CS    |
| 1005       | BI    |

student_grade:

| student_id | course | semester    | grade |
|------------|--------|-------------|-------|
| 1001       | CS.201 | 2016.Spring | 100   |
| 1001       | CS.202 | 2016.Fall   | 90    |
| 1001       | EE.201 | 2016.Spring | 90    |
| 1002       | CS.201 | 2016.Spring | 70    |
| 1002       | CS.202 | 2016.Fall   | 70    |
| 1003       | CS.201 | 2016.Spring | 99    |
| 1003       | EE.201 | 2016.Fall   | 90    |
| 1003       | CS.202 | 2016.Fall   | 90    |
| 1004       | CS.201 | 2016.Spring | 99    |
| 1004       | BI.202 | 2016.Fall   | 80    |
| 1005       | CS.201 | 2017.Spring | 100   |

Now I want to select top 2 students major in CS grade of CS.201 in 2016.Spring , so the results may looks like this:

| student_id | major | semester    | course | grade |
|------------|-------|-------------|--------|-------|
| 1001       | CS    | 2016.Spring | CS.201 | 100   |
| 1003       | CS    | 2016.Spring | CS.201 | 99    |
| 1004       | CS    | 2016.Spring | CS.201 | 99    |

Note that since there're 2 students who got 99 in CS.201 , so we want all 3 records (not just using limit(2) ).

The database is MySQL.

My sql script look like :

SELECT student_info.student_id, 
               student_info.major, 
               student_grade.semester, 
               student_grade.course, 
               student_grade.grade
FROM student_info, student_grade
WHERE student_info.major = 'CS'
  AND student_info.student_id = student_grade.student_id
  AND student_grade.semester = '2016.Spring'
  AND student_grade.course = 'CS.201'
ORDER BY student_grade.grade DESC
LIMIT 2

The question need to use DENSE_RANK function,unfortunately DENSE_RANK function only support version higher than MySQL 8.0 .

You need to make the rank on student_grade ,so you can write subquery to create rank result set, then join on student_info

SELECT b.student_id, 
       b.major, 
       a.semester, 
       a.course, 
       a.grade 
FROM   (SELECT student_id, 
               grade, 
               semester, 
               course, 
               @prev := @curr, 
               @curr := grade, 
               @rank := IF(@prev = @curr, @rank, @rank + 1) AS rank 
        FROM   student_grade, 
               (SELECT @curr := NULL, 
                       @prev := NULL, 
                       @rank := 0) s 
        WHERE  course = 'CS.201' 
               AND semester = '2016.Spring' 
        ORDER  BY grade DESC) a 
       INNER JOIN student_info b 
               ON a.student_id = b.student_id 
WHERE  a.rank <= 2 
       AND b.major = 'CS' 

sqlfiddle: https://www.db-fiddle.com/f/tkU4UfRE3AZziiEn4HiLwF/0

Explanation:

  • @prev := @curr let @curr be previous grade
  • @curr := grade set current grade in @curr variable
  • @rank := IF(@prev = @curr, @rank, @rank+1) AS rank to check @prev variable and @curr variable are the same.if they are same used same rank otherwise rank + 1

You can try mysql window function with DENSE_RANK() . You can refer documentation here .

SELECT * FROM
(
    SELECT
        a.*,
        DENSE_RANK() OVER w AS 'rank'
    FROM

    (
        SELECT
            g.student_id,
            i.major,
            g.course,
            g.semester,
            g.grade
        FROM 
            student_grade g
            JOIN student_info i
                ON i.student_id = g.student_id

        WHERE i.major = 'CS'
            AND g.semester = '2016.Spring'
            AND g.course = 'CS.201'
    ) a

    WINDOW w AS (ORDER BY a.grade DESC)
) b

WHERE b.rank <= 2;

Hope this help, good luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM