整体思路:与「获得积分最多的人(二)」如出一辙,唯一需要注意的add的时候需要+,reduce的时候需要-,那么在构造新表new_grade
的时候需要创建临时表t
,逻辑如下:
- 包含两个列 user_id 和 new_num,针对于new_num:
- 如果type='add',那么直接取grade_num的值
- 如果type='reduce',那么取grade_num的负值
SELECT user_id, case type when 'add' then grade_num when 'reduce' then (-1 * grade_num) end AS new_num FROM grade_info
在临时表t
的基础上得到包含所有用户总积分的表,记录为new_grade
,逻辑如下:
WITH new_grade AS ( SELECT t.user_id, sum(t.new_num) AS grade_sum FROM (SELECT user_id, case type when 'add' then grade_num when 'reduce' then (-1 * grade_num) end AS new_num FROM grade_info) t GROUP BY t.user_id )
接着,利用dense_rank排名函数对grade_sum列按降序排名(注意,这是MySQL8.0往后的特性),得到一张临时表t1
,随后与user
表进行左连接(大表在前,小表在后),最后where中写上判断逻辑为只取排名为1的数据,完整代码如下:
WITH new_grade AS ( SELECT t.user_id, sum(t.new_num) AS grade_sum FROM (SELECT user_id, case type when 'add' then grade_num when 'reduce' then (-1 * grade_num) end AS new_num FROM grade_info) t GROUP BY t.user_id ) SELECT t1.user_id AS id, t2.name, t1.grade_sum FROM (SELECT user_id, grade_sum, dense_rank() over(order by grade_sum desc) AS d_rank FROM new_grade) t1 LEFT JOIN `user` t2 ON t1.user_id = t2.id WHERE d_rank = 1 ORDER BY t1.user_id
写在最后:
- 利用
WITH table_name AS ()
的形式可以简化代码量,使得逻辑更加清晰。 - dense_rank、rank、row_number三个排名函数的区别需要牢记于心,做到灵活应用。