您的位置:首页 > 数据库 > SQL

SQL之分组排序取top n

2017-09-27 13:08 232 查看
转自:http://blog.csdn.net/wguangliang/article/details/50167283

要求:按照课程分组,查找每个课程最高的两个成绩。

数据文件如下:

第一列no为学号,第二列course为课程,第三列score为分数

[plain] view plain copy

1. mysql> select * from lesson;

2. +-------+---------+-------+

3. | no | course | score |

4. +-------+---------+-------+

5. | N0101 | Marth | 100 |

6. | N0102 | English | 12 |

7. | N0102 | Chinese | 55 |

8. | N0102 | History | 58 |

9. | N0102 | Marth | 25 |

10. | N0103 | English | 100 |

11. | N0103 | Chinese | 87 |

12. | N0103 | History | 88 |

13. | N0103 | Marth | 72 |

14. | N0104 | English | 20 |

15. | N0104 | Chinese | 60 |

16. | N0104 | History | 88 |

17. | N0104 | Marth | 56 |

18. | N0105 | English | 56 |

19. | N0105 | Chinese | 88 |

20. | N0105 | History | 88 |

21. | N0201 | English | 66 |

22. | N0201 | Chinese | 77 |

23. | N0201 | History | 80 |

24. | N0201 | Marth | 100 |

25. | N0202 | English | 35 |

26. | N0202 | Chinese | 56 |

27. | N0202 | History | 86 |

28. | N0202 | Marth | 99 |

29. | N0203 | English | 100 |

30. | N0203 | Chinese | 87 |

31. | N0203 | History | 88 |

32. | N0203 | Marth | 57 |

33. | N0204 | English | 98 |

34. | N0204 | Chinese | 100 |

35. | N0204 | History | 66 |

36. | N0204 | Marth | 71 |

37. | N0205 | English | 98 |

38. | N0205 | Chinese | 100 |

39. | N0205 | History | 66 |

40. | N0205 | Marth | 71 |

41. | N0301 | English | 66 |

42. | N0301 | Chinese | 89 |

43. | N0301 | History | 68 |

44. | N0301 | Marth | 83 |

45. | N0302 | English | 76 |

46. | N0302 | Chinese | 99 |

47. | N0302 | History | 80 |

48. | N0302 | Marth | 74 |

49. | N0303 | English | 100 |

50. | N0303 | Chinese | 100 |

51. | N0303 | History | 88 |

52. | N0303 | Marth | 57 |

53. | N0304 | English | 76 |

54. | N0304 | Chinese | 100 |

55. | N0304 | History | 66 |

56. | N0304 | Marth | 86 |

57. | N0305 | English | 98 |

58. | N0305 | Chinese | 100 |

59. | N0305 | History | 40 |

60. | N0305 | Marth | 59 |

61. | N0306 | English | 52 |

62. | N0306 | Chinese | 87 |

63. | N0306 | History | 72 |

64. | N0306 | Marth | 71 |

65. | N0101 | Chinese | 55 |

66. | N0101 | History | 84 |

67. | N0101 | English | 82 |

68. | N0101 | English | 82 |

69. +-------+---------+-------+

70. 64 rows in set

在hive上查询

1. select a.course,a.score

2. from

3. (

4. select course,score,row_number() over(partition by course order by score desc) as n

5. from lesson

6. )a

7. where a.n<=2;

其中:

1. row_number() over(partition by course order by score desc)

意思是以课程分组,按成绩递减排序,并为每组中的数据打上行号的标记,从1开始。

这样,再在外层套一层过滤行号小于等于2的即可:-D

查询结果如下图1所示:





图1 Hive查询结果

在mysql上查询

由于MySQL不支持row_number()over()等窗口函数

方法1.自查询比较

1. select course,score

2. from lesson a

3. where 2 >

4. (

5. select count(1)

6. from lesson b

7. where a.score<b.score and a.course=b.course

8. )

9. order by a.course,a.score desc;

因为是查询最高的两个成绩,所以是2>,如果查询最高的前N个成绩,改成 N>

该条sql语句的大概思路是:

从a表中拿出一条数据,与b表中所有与该条数据相同course的数据比较,统计出b表有多少相同课程的score比该条数据的score高;

如果b表中有0条比该条数据高,则该条数据是该门课程的最高分;

如果统计出有1条数据,则该条数据是该门课程分数的第二高;

但是,还存在一些问题:

比如,最高分存在多个,则会统计出多于2条的数据,如下图2统计结果也有所反应:





图2 mysql查询结果

方法2.动态sql

1. SET @row=0;

2. SET @groupid='';

3. select a.course,a.score

4. from

5. (

6. select no,course,score,case when @groupid=course then @row:=@row+1 else @row:=1 end rownum,@groupid:=course from lesson

7. order by course,score desc

8. )a

9. where a.rownum<=2;

其中:

@row用于统计行号,@groupid用于分组,记录该组的名称

1. select no,course,score,case when @groupid=course then @row:=@row+1 else @row:=1 end rownum,@groupid:=course from lesson

2. order by course,score desc

意思是:按照分组名course和需要的排序score递增 进行排序,这样,相同课程就会排在一起,且相同的课程之间按照成绩排序。

取出一条数据,如果该条数据的course与@group相同,则意味着是相同课程之间的比较,那么@row自加1。

否则意味着该条数据是另一门课程的第一条数据,则@row=1

这样每个课程就能够按照成绩排序并标记上行号

那么外层只需要过滤rownum<=2即可得到每门课的前2个最高分。

最后执行结果与hive一致,不再上图片了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: