广东省特色专业建设网站,建设银行e路通网网站,百度网址大全网址导航大全,珠海网站建设科速互联背景
考试批次班级姓名语文202302三年一班张小明130.00202302三年一班王二小128.00202302三年一班谢春花136.00202302三年二班冯世杰129.00202302三年二班马功成130.00202302三年二班魏翩翩136.00
假设我们有如上数据#xff0c;现在有一个需求需要统计各学生语文单科成绩在班…背景
考试批次班级姓名语文202302三年一班张小明130.00202302三年一班王二小128.00202302三年一班谢春花136.00202302三年二班冯世杰129.00202302三年二班马功成130.00202302三年二班魏翩翩136.00
假设我们有如上数据现在有一个需求需要统计各学生语文单科成绩在班级中的排名和全年段排名你会如何实现
很容易的我们想到了 rank() over() 实现
over()是分析函数可以和 rank()、 dense_rank() 、 row_number() 配合使用。
复制代码
使用语法如下
RANK() OVER(PARTITION BY COLUMN ORDER BY COLUMN)
dense_rank() OVER(PARTITION BY COLUMN ORDER BY COLUMN)
ROW_NUMBER() OVER(PARTITION BY COLUMN ORDER BY COLUMN)
复制代码
解释partition by用于给结果集分组如果没有指定那么它把整个结果集作为一个分组。
rank()涵数主要用于排序并给出序号 对于排序并列的数据给予相同序号并空出并列所占的名次。dense_rank() 功能同rank()一样区别在于不空出并列所占的名次row_number()涵数则是按照顺序依次使用 不考虑并列rank 结果为 1,2,2,4 dense_rank 结果为 1,2,2,3 row_number 结果为 1,2,3,4 实际应用中会存在数据从其他外部系统接入且数据量不大等多种情况那么使用Java代码的方式实现分组排名的功能则显得更加方便。
详细设计及实现
排序定义类 OrderBy
public class OrderBy {private String orderByEL;/*** 是否升序*/private boolean ascend;public OrderBy(){//默认升序this.ascend true;}public String orderByEL(){return this.orderByEL;}public OrderBy orderByEL(String orderByEL){this.orderByEL orderByEL;return this;}public OrderBy ascend(boolean ascend){this.ascend ascend;return this;}public boolean ascend(){return this.ascend;}
}
复制代码
该类定义了如下属性
排序的fileld是否升序
获取排名方法
该方法定义如下
T void rankOver(ListT dataList, String[] partitionByFields, ListOrderBy orderByList, String resultField, int rankType);
复制代码
该方法提供了5个入参
dataList 排序的数据集partitionByFields 分组field的数组orderByList 排序字段集合resultField 排名结果存放的字段rankType 排名方式 1:不考虑并列row_number 结果为 1,2,3,42:考虑并列空出并列所占的名次rank 结果为 1,2,2,43:考虑并列不空出并列所占的名次dense_rank 1,2,2,3
该方法具体实现如下 public static T void rankOver(ListT dataList, String[] partitionByFields, ListOrderBy orderByList, String resultField, int rankType) {if (CollectionUtils.isEmpty(orderByList)) {return;}//STEP_01 剔除掉不参与排名的数据ListT tempList new ArrayList();for (T data : dataList) {boolean part true;for (OrderBy rptOrderBy : orderByList) {Object o1 executeSpEL(rptOrderBy.orderByEL(), data);if (o1 null) {//参与排序的值为null的话则不参与排名part false;break;}}if (part) {tempList.add(data);}}if (CollectionUtils.isEmpty(tempList)) {return;}//STEP_02 分组MapString, ListT groupMap group(tempList, null, partitionByFields);for (ListT groupDataList : groupMap.values()) {order(orderByList, groupDataList);if (rankType 1) {int rank 1;for (T temp : groupDataList) {setFieldValue(temp, resultField, rank);rank;}} else {int prevRank Integer.MIN_VALUE;int size groupDataList.size();for (int i 0; i size; i) {T current groupDataList.get(i);if (i 0) {//第一名setFieldValue(current, resultField, 1);prevRank 1;} else {T prev groupDataList.get(i - 1);boolean sameRankWithPrev true;//并列排名for (OrderBy rptOrderBy : orderByList) {Object o1 executeSpEL(rptOrderBy.orderByEL(), current);Object o2 executeSpEL(rptOrderBy.orderByEL(), prev);if (!o1.equals(o2)) {sameRankWithPrev false;break;}}if (sameRankWithPrev) {setFieldValue(current, resultField, getFieldValue(prev, resultField));if (rankType 2) {prevRank;}} else {setFieldValue(current, resultField, prevRank);}}}}}}
复制代码
使用案例
定义一个学生类
public class Student {private String batch;private String banji;private String name;private Double yuwen;//extraprivate Integer rank1;private Integer rank2;public Student(String batch, String banji, String name, Double yuwen) {this.batch batch;this.banji banji;this.name name;this.yuwen yuwen;}
}复制代码
我们写一个方法返回如下数据
public ListStudent getDataList() {ListStudent dataList new ArrayList();dataList.add(new Student(202302, 三年一班, 张小明, 130.0));dataList.add(new Student(202302, 三年一班, 王二小, 128.0));dataList.add(new Student(202302, 三年一班, 谢春花, 136.0));dataList.add(new Student(202302, 三年二班, 冯世杰, 129.0));dataList.add(new Student(202302, 三年二班, 马功成, 130.0));dataList.add(new Student(202302, 三年二班, 魏翩翩, 136.0));return dataList;
}
复制代码
获取学生语文成绩的班级排名和年段排名排名采用并列并空出并列所占用名次的方式。
ListStudent dataList getDataList();
ListOrderBy orderByList new ArrayList();
orderByList.add(new OrderBy().orderByEL(yuwen).ascend(false));
//获取全校排名
DataProcessUtil.rankOver(dataList, new String[]{batch}, orderByList, rank1, 2);
//获取班级排名
DataProcessUtil.rankOver(dataList, new String[]{batch, banji}, orderByList, rank2, 2);
log(语文单科成绩排名情况如下);MapString, ListStudent groupMap DataProcessUtil.group(dataList, null, new String[]{batch});
for (Map.EntryString, ListStudent entry : groupMap.entrySet()) {log(考试批次 entry.getKey());for (Student s : entry.getValue()) {log(String.format(班级%s 学生%s 语文成绩%s 班级排名%s 全校排名%s, s.getBanji(), s.getName(), s.getYuwen(), s.getRank2(), s.getRank1()));}log();
}
复制代码
结果如下
语文单科成绩排名情况如下
考试批次202302
班级三年一班 学生张小明 语文成绩130.0 班级排名2 全校排名3
班级三年一班 学生王二小 语文成绩128.0 班级排名3 全校排名6
班级三年一班 学生谢春花 语文成绩136.0 班级排名1 全校排名1
班级三年二班 学生冯世杰 语文成绩129.0 班级排名3 全校排名5
班级三年二班 学生马功成 语文成绩130.0 班级排名2 全校排名3
班级三年二班 学生魏翩翩 语文成绩136.0 班级排名1 全校排名1
复制代码
可以看到全校排名中 有两个并列第一名 两个并列第三名且空出了并列所占用的名次2 和 名次4