一条sql让你学会java中的lambda
引:
关于lambda表达式看了很多博客,也看了相关视频,看的时候感觉都会,实操还是出了问题,最近写了一条复杂sql,想着用lambda实现相同效果,前前后后花了不少时间,总算搞懂了lambda的基本使用,废话少说,一起来看看。
题目:
涉及数据库表:人员表NMG. Emp_Info
字段名称 | 类型 | 说明 |
---|---|---|
Education | 文本 | 学历 |
Department_Code | 关联 | 关联病区编码 |
Enabled | 文本 | 状态(0禁用1启用) |
JoinTime | 时间类型 | 入职时间 |
病区表dbo. MD_Ward
字段名称 | 类型 | 说明 |
---|---|---|
code | 文本 | 病区编码 |
Name | 文本 | 病区名称 |
ID | 文本 | 病区ID |
要求:
查询所有病区下的人员根据入职时间分成 1年以内,1-2年以内,3-4以内,5-10以内,10-20以内,>20年 六个阶段的人员数量
筛选条件:
人员状态=1
病区ID !=72 and ID!=79 and ID!=236
mysql实现
代码
SELECT W.Name AS 病区,
COUNT(*) AS 人员数量,
SUM(CASE WHEN DATEDIFF(YEAR, E.JoinTime, GETDATE()) < 1 THEN 1 ELSE 0 END) AS 一年以内,
SUM(CASE WHEN DATEDIFF(YEAR, E.JoinTime, GETDATE()) BETWEEN 1 AND 2 THEN 1 ELSE 0 END) AS 一到二年以内,
SUM(CASE WHEN DATEDIFF(YEAR, E.JoinTime, GETDATE()) BETWEEN 3 AND 4 THEN 1 ELSE 0 END) AS 三到四年以内,
SUM(CASE WHEN DATEDIFF(YEAR, E.JoinTime, GETDATE()) BETWEEN 5 AND 10 THEN 1 ELSE 0 END) AS 五到十年以内,
SUM(CASE WHEN DATEDIFF(YEAR, E.JoinTime, GETDATE()) BETWEEN 10 AND 20 THEN 1 ELSE 0 END) AS 十到二十年以内,
SUM(CASE WHEN DATEDIFF(YEAR, E.JoinTime, GETDATE()) > 20 THEN 1 ELSE 0 END) AS 大于20年
FROM NMG.Emp_Info AS E
JOIN dbo.MD_Ward AS W ON E.Department_Code = W.code
WHERE E.Enabled = '1'
AND W.ID NOT IN (72, 79, 236)
GROUP BY W.Name;
执行结果
统计一下人员总数,方便后面对照结果(总数276)
lambda实现
ok,做完这些现在用lambda实现一下,因为lambda不好做连接查询(chatgpt生成出来使用lambda实现连接看不懂)我们就用mysql做连接查询
执行sql
SELECT
dbo.MD_Ward.code,
dbo.MD_Ward.Name,
dbo.MD_Ward.ID,
NMG.Emp_Info.Education,
NMG.Emp_Info.Enabled,
NMG.Emp_Info.JoinTime
FROM
dbo.MD_Ward
LEFT JOIN NMG.Emp_Info ON NMG.Emp_Info.Department_Code = dbo.MD_Ward.code
结果
过滤(filter)
allList的数据
过滤掉id=72(其他过滤条件大同小异)
分组(终结方法中的Collectors.groupingBy)
按照病区进行分组
代码实现
Map<String, List<All>> collect = allList.stream()
.filter(list -> "1".equals(list.getEnabled()))
.filter(list -> !StringUtils.isEmpty(list.getJoinTime()))
.filter(list -> !StringUtils.isEmpty(list.getName()))
.filter(list -> !Arrays.asList("72", "79", "236").contains(list.getiD()))
.collect(Collectors.groupingBy(All::getName));
collect中的数据
现在最简单的过滤做完了,那么接下来需要按照JionTime对人员进行分组,一种是将collect集合循环,拿到每一个时间与当前时间相减,算出年限。另一种是lambda实现,我两种都写一下。
循环实现:
Map<String, Object> kmap = new HashMap<>();
collect.forEach((k, v) -> {
Map<String, List<All>> vmap = new HashMap<>();
vmap.put("1年以内", new ArrayList<>());
vmap.put("1-2年以内", new ArrayList<>());
vmap.put("3-4以内", new ArrayList<>());
vmap.put("5-10以内", new ArrayList<>());
vmap.put("10-20以内", new ArrayList<>());
vmap.put(">20年", new ArrayList<>());
for (All all : v) {
if (StringUtils.isEmpty(all.getJoinTime())) {
break;
}
LocalDate localDate =
NbUtil.getDt(all.getJoinTime()).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
long years = ChronoUnit.YEARS.between(localDate, LocalDate.now());
if (years < 1) {
List<All> allList1 = vmap.get("1年以内");
allList1.add(all);
} else if (years >= 1 && years <= 2) {
List<All> allList1 = vmap.get("1-2年以内");
allList1.add(all);
} else if (years >= 3 && years <= 4) {
List<All> allList1 = vmap.get("3-4以内");
allList1.add(all);
} else if (years >= 5 && years <= 10) {
List<All> allList1 = vmap.get("5-10以内");
allList1.add(all);
} else if (years >= 10 && years <= 20) {
List<All> allList1 = vmap.get("10-20以内");
allList1.add(all);
} else {
List<All> allList1 = vmap.get(">20年");
allList1.add(all);
}
}
kmap.put(k, vmap);
});
System.out.println(kmap);
得到的kmap
lambda实现
在写代码之前,先思考一下如果是你,你会怎么写这段lambda代码呢?
将一下我的思路,先统计每个病区的年限,得到一个年限数字字符串,在对年限进行统计。
终结操作(reduce)map reduce
不会不懂reduce看一节 三根reduce(一)就够了
通过map拿到日期数据
reduce处理拿到年限,并且进行拼接
代码
Map<String, String> map = new HashMap<>();
collect.forEach((k, v) -> {
String reduce = v.stream()
.map(list -> list.getJoinTime())
.reduce("", (初始值, joinTime) -> {
LocalDate localDate = NbUtil.getDt(joinTime).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
return 初始值 + "," + String.valueOf(ChronoUnit.YEARS.between(localDate, LocalDate.now()));
});
System.out.println(k + "," + reduce + ":" + (reduce.split(",").length - 1));
map.put(k, reduce);
});
结果
这时候差不多就结束了,拿到字符串split(“,”)一下,在对集合循环处理一下就好(其他办法没想出来)
如果我写lambda上瘾了想用lambda处理怎么办?可以这样实现。
代码
map.forEach((k, v) -> {
List<Map<String, Integer>> mapList = new ArrayList<>();
Map<String, Integer> mapp = new HashMap<>();
Integer year1 = Arrays.stream(v.split(","))
.filter(list -> !StringUtils.isEmpty(list))
.map(list -> Integer.valueOf(list))
.reduce(0, (identity, year) -> {
if (year < 1) {
return identity + 1;
} else {
return identity;
}
});
mapp.put("一年以内", year1);
Integer year2 = Arrays.stream(v.split(","))
.filter(list -> !StringUtils.isEmpty(list))
.map(list -> Integer.valueOf(list))
.reduce(0, (identity, year) -> {
if (year >= 1 && year <= 2) {
return identity + 1;
} else {
return identity;
}
});
mapp.put("一到二年以内", year2);
}
多余的就不写了,可以看出来 还是比较麻烦的,不如循环。
验证
统计一下总数
代码
final int[] count = new int[1];
System.out.println("count:" + count[0]);
result.forEach((k, v) -> {
for (Map<String, Integer> stringIntegerMap : v) {
stringIntegerMap.forEach((key, value) -> {
count[0] = count[0] + value;
System.out.println(k + "," + key + ":" + value);
});
}
});
System.out.println("count:" + count[0]);
结果
276,与mysql执行的一样,数据我也对过,没问题。
总结
lambda方法有很多,我只讲了与我这段sql相关的,其他用到的时候看一下就OK了。