如何将SQL GROUP BY和聚合转换为Java 8

我无法抗拒。 我已经阅读了Hugo Prudente在Stack Overflow上提出的问题 。 而且我知道必须有比JDK提供的更好的方法。

问题如下:

我正在寻找一个lambda来优化已检索的数据。 我有一个原始的结果集,如果用户不更改我想要的日期,则使用Java的lambda来对结果进行分组。 我对使用Java的lambdas还是陌生的。

我正在寻找的lambda与此查询类似的作品。

SELECTz, w, MIN(x), MAX(x), AVG(x), MIN(y), MAX(y), AVG(y) 
FROM table 
GROUP BY z, w;

函数编程不是。

在进行讨论之前,让我们建立一个非常重要的事实。 SQL是一种完全声明性的语言。 Java 8之类的功能性(或“功能性”语言,以使Haskell爱好者保持和平)不是声明性的。 尽管使用函数来表达数据转换算法要比使用对象来表达更为简洁,或更糟糕的是使用命令式指令来表达它们,但您仍在明确地表达算法。

编写SQL时,您不会编写任何算法。 您只需描述您想要的结果。 SQL引擎的优化程序将为您找出算法-例如,基于您可能在Z但在W(Z, W)上没有索引的事实。

尽管可以使用Java 8轻松实现此类简单示例,但一旦需要进行更复杂的报告,您将很快遇到Java的局限性。

当然,正如我们之前写过的,将SQL和函数式编程结合起来可以达到最佳效果 。

如何用Java 8编写?

有多种方法可以做到这一点。 本质是要了解这种转变中的所有参与者。 而且,不管您发现这是简单还是困难(适合Java 8或不足),思考新Stream API的不同,鲜为人知的部分无疑都是值得的。

这里的主要参与者是:

  • Stream :如果您使用的是JDK 8库,那么新的java.util.stream.Stream类型将是您的首选。
  • 收集器 :JDK为我们提供了一个相当低层的,因此非常强大的新API,用于数据聚合(也称为“缩减”)。 该API由新的java.util.stream.Collector类型进行了总结,到目前为止,我们在Blogosphere中仅听到了很少的新类型

免责声明

这里显示的某些代码可能无法在您喜欢的IDE中使用。 不幸的是,即使Java 7寿终正寝,所有主要的IDE(Eclipse,IntelliJ,NetBeans),甚至javac编译器仍然存在很多与泛型类型推断和lambda表达式组合有关的错误。 敬请期待,直到修复了这些错误! 并报告您发现的任何错误。 我们都会感谢您!

我们走吧!

让我们回顾一下我们的SQL语句:

SELECTz, w, MIN(x), MAX(x), AVG(x), MIN(y), MAX(y), AVG(y) 
FROM table 
GROUP BY z, w;

Stream API而言,表本身就是Stream 。 让我们假设我们有一个“表类型” A

class A {final int w;final int x;final int y;final int z;A(int w, int x, int y, int z) {this.w = w;this.x = x;this.y = y;this.z = z;}@Overridepublic String toString() {return "A{" +"w=" + w +", x=" + x +", y=" + y +", z=" + z +'}';}
}

如果需要,还可以添加equals()hashCode()

现在,我们可以使用Stream.of()和一些示例数据轻松组成Stream

Stream<A> stream =
Stream.of(new A(1, 1, 1, 1),new A(1, 2, 3, 1),new A(9, 8, 6, 4),new A(9, 9, 7, 4),new A(2, 3, 4, 5),new A(2, 4, 4, 5),new A(2, 5, 5, 5));

现在,下一步是GROUP BY z, w 。 不幸的是, Stream API本身不包含这种便捷方法。 我们必须通过指定更通用的Stream.collect()操作,并将一个Collector传递给它进行分组,来诉诸于更底层的操作。 幸运的是, Collectors帮助Collectors类中已经提供了各种不同的分组Collectors

因此,我们将其添加到stream

Stream.of(new A(1, 1, 1, 1),new A(1, 2, 3, 1),new A(9, 8, 6, 4),new A(9, 9, 7, 4),new A(2, 3, 4, 5),new A(2, 4, 4, 5),new A(2, 5, 5, 5))
.collect(Collectors.groupingBy(...));

jool-logo-黑色 现在开始有趣的部分。 我们如何指定我们要同时按AzAw分组? 我们需要为该groupingBy方法提供一个函数,该函数可以从A类型提取诸如SQL 元组之类的东西。 我们可以编写自己的元组,也可以简单地使用jOOλ的元组, jOOλ 是我们创建并开源的库,用于改进jOOQ集成测试 。

Tuple2类型大致如下:

public class Tuple2<T1, T2> {public final T1 v1;public final T2 v2;public T1 v1() {return v1;}public T2 v2() {return v2;}public Tuple2(T1 v1, T2 v2) {this.v1 = v1;this.v2 = v2;}
}public interface Tuple {static <T1, T2> Tuple2<T1, T2> tuple(T1 v1, T2 v2) {return new Tuple2<>(v1, v2);}
}

它具有许多有用的功能,但是这些功能对于本文而言已足够。

在旁注

为什么JDK没有附带诸如C#或Scala's的内置元组, 这让我无所适从。

没有元组的函数式编程就像没有糖的咖啡:苦涩的表情。

反正…回到正轨

因此,我们按照(Az, Aw)元组进行分组,就像在SQL中一样

Map<Tuple2<Integer, Integer>, List<A>> map =
Stream.of(new A(1, 1, 1, 1),new A(1, 2, 3, 1),new A(9, 8, 6, 4),new A(9, 9, 7, 4),new A(2, 3, 4, 5),new A(2, 4, 4, 5),new A(2, 5, 5, 5))
.collect(Collectors.groupingBy(a -> tuple(a.z, a.w)
));

如您所见,这将产生一个冗长但非常具有描述性的类型,一个映射包含我们的分组元组作为其键,并以收集到的表记录的列表作为其值。

运行以下语句:

map.entrySet().forEach(System.out::println);

将产生:

(1, 1)=[A{w=1, x=1, y=1, z=1}, A{w=1, x=2, y=3, z=1}]
(4, 9)=[A{w=9, x=8, y=6, z=4}, A{w=9, x=9, y=7, z=4}]
(5, 2)=[A{w=2, x=3, y=4, z=5}, A{w=2, x=4, y=4, z=5}, A{w=2, x=5, y=5, z=5}]

那已经很棒了! 实际上,它的行为类似于SQL:2011标准COLLECT()聚合函数,该函数在Oracle 10g +中也可用

现在,我们实际上不是汇总A记录,而是汇总xy的各个值。 JDK为我们提供了两个有趣的新类型,例如java.util.IntSummaryStatistics ,可通过Collectors.summarizingInt()Collectors类型再次方便使用。

附带说明

就我的口味而言,这种大锤数据聚合技术有点古怪。 JDK库被故意保留为低级和冗长的,可能是为了减小库的占用空间,或者是为了防止在5-10年内(在JDK 9和10发行之后)“可怕的”后果。 可能已经过早添加 。

同时,这个IntSummaryStatistics全部或全都不IntSummaryStatistics ,它盲目地为您的集合聚合了这些流行的聚合值:

  • COUNT(*)
  • SUM()
  • MIN()
  • MAX()

很明显,一旦有了SUM()COUNT(*) ,就也有AVG() = SUM() / COUNT(*) 。 所以这将是Java方式。 IntSummaryStatistics

如果您想知道,SQL:2011标准指定了以下聚合函数:

AVG, MAX, MIN, SUM, EVERY, ANY, SOME, COUNT, STDDEV_POP, STDDEV_SAMP, VAR_SAMP, VAR_POP, COLLECT, FUSION, INTERSECTION, COVAR_POP, COVAR_SAMP, CORR, REGR_SLOPE, REGR_INTERCEPT, REGR_COUNT, REGR_R2, REGR_AVGX, REGR_AVGY, REGR_SXX, REGR_SYY, REGR_SXY, PERCENTILE_CONT, PERCENTILE_DISC, ARRAY_AGG

很明显,SQL中还有许多其他特定于供应商的聚合和窗口函数 。 我们已经在博客上发布了所有内容:

  • 可能最酷的SQL功能:窗口函数
  • 如何使用逆分布函数模拟MEDIAN()聚合函数
  • 很棒的PostgreSQL 9.4 / SQL:2003 FILTER子句,用于聚合函数
  • 您还不知道的真正的SQL宝石:EVERY()聚合函数
  • 您真的了解SQL的GROUP BY和HAVING子句吗?
  • 不要错过具有FIRST_VALUE(),LAST_VALUE(),LEAD()和LAG()的超凡SQL能力
  • CUME_DIST(),一个鲜为人知的SQL宝石

的确如此, MIN, MAX, SUM, COUNT, AVG无疑是最受欢迎的。 但是如果它们没有包含在这些默认聚合类型中,而是以一种更加可组合的方式提供,那就更好了。

反正…回到正轨

如果您想保持低水平并主要使用JDK API,则可以使用以下技术在两列上实现聚合:

Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics>
> map = Stream.of(new A(1, 1, 1, 1),new A(1, 2, 3, 1),new A(9, 8, 6, 4),new A(9, 9, 7, 4),new A(2, 3, 4, 5),new A(2, 4, 4, 5),new A(2, 5, 5, 5))
.collect(Collectors.groupingBy(a -> tuple(a.z, a.w),Collector.of(// When collecting, we'll aggregate data// into two IntSummaryStatistics for x and y() -> tuple(new IntSummaryStatistics(), new IntSummaryStatistics()),// The accumulator will simply take// new t = (x, y) values(r, t) -> {r.v1.accept(t.x);r.v2.accept(t.y);},// The combiner will merge two partial// aggregations, in case this is executed// in parallel(r1, r2) -> {r1.v1.combine(r2.v1);r1.v2.combine(r2.v2);return r1;})
));map.entrySet().forEach(System.out::println);

现在上面将打印

(1, 1)=(IntSummaryStatistics{count=2, sum=3, min=1, average=1.500000, max=2}, IntSummaryStatistics{count=2, sum=4, min=1, average=2.000000, max=3})
(4, 9)=(IntSummaryStatistics{count=2, sum=17, min=8, average=8.500000, max=9}, IntSummaryStatistics{count=2, sum=13, min=6, average=6.500000, max=7})
(5, 2)=(IntSummaryStatistics{count=3, sum=12, min=3, average=4.000000, max=5}, IntSummaryStatistics{count=3, sum=13, min=4, average=4.333333, max=5})

但是显然,没有人愿意写那么多代码。 用jOOλ可以用更少的代码来实现相同的目的

Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics>
> map =// Seq is like a Stream, but sequential only,
// and with more features
Seq.of(new A(1, 1, 1, 1),new A(1, 2, 3, 1),new A(9, 8, 6, 4),new A(9, 9, 7, 4),new A(2, 3, 4, 5),new A(2, 4, 4, 5),new A(2, 5, 5, 5))// Seq.groupBy() is just short for 
// Stream.collect(Collectors.groupingBy(...))
.groupBy(a -> tuple(a.z, a.w),// ... because once you have tuples, // why not add tuple-collectors?Tuple.collectors(Collectors.summarizingInt(a -> a.x),Collectors.summarizingInt(a -> a.y))
));

您在上面看到的内容可能与原始的非常简单的SQL语句非常接近:

SELECTz, w, MIN(x), MAX(x), AVG(x), MIN(y), MAX(y), AVG(y) 
FROM table 
GROUP BY z, w;

这里有趣的部分是我们拥有所谓的“元组收集器”,这是一个Collector ,它可以针对任何程度的元组(最多8个)将数据收集到汇总结果的元组中。 这是Tuple.collectors的代码:

// All of these generics... sheesh!
static <T, A1, A2, D1, D2> Collector<T, Tuple2<A1, A2>, Tuple2<D1, D2>> 
collectors(Collector<T, A1, D1> collector1, Collector<T, A2, D2> collector2
) {return Collector.of(() -> tuple(collector1.supplier().get(), collector2.supplier().get()),(a, t) -> {collector1.accumulator().accept(a.v1, t);collector2.accumulator().accept(a.v2, t);},(a1, a2) -> tuple(collector1.combiner().apply(a1.v1, a2.v1), collector2.combiner().apply(a1.v2, a2.v2)),a -> tuple(collector1.finisher().apply(a.v1), collector2.finisher().apply(a.v2)));
}

其中Tuple2<D1, D2>是我们从collector1 (提供D1 )和collector2 (提供D2 )派生的聚合结果类型。

而已。 大功告成!

结论

Java 8是迈向Java函数编程的第一步。 使用Streams和lambda表达式,我们已经可以完成很多工作。 但是,JDK API的级别极低,使用诸如Eclipse,IntelliJ或NetBeans之类的IDE时的体验仍然有些令人沮丧。 在撰写本文(并添加Tuple.collectors()方法)时,我已经向不同的IDE报告了大约10个错误。 在JDK 1.8.0_40之前,某些javac编译器错误尚未修复。 换一种说法:

我只是不断地向泛滥的对象抛出泛型类型参数,直到编译器停止对我不利为止

但是,我们走的很好。 我相信JDK 9(尤其是JDK 10)将附带更多有用的API,届时上述所有内容都有望从新的值类型和泛型类型专门化中受益。

jool-logo-黑色 我们创建了jOOλ,将缺少的片段添加到JDK库中。 如果您想全神贯注地进行函数式编程,即当您的词汇表包含诸如monads,monoids,functors之类的时髦术语(无法抗拒)时,我们建议您完全跳过JDK的Streams和jOOλ,然后下载functionaljava 马克·佩里 ( Mark Perry)或丹尼尔·迪特里希 ( Daniel Dietrich)的 javaslang

翻译自: https://www.javacodegeeks.com/2015/01/how-to-translate-sql-group-by-and-aggregations-to-java-8.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/361366.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

类库,随机数,属性,值类型和引用类型

类库&#xff0c;随机数&#xff0c;属性&#xff0c;值类型和引用类型 1.文档注释与类库的生成 (1)类库的生成 右击添加新建项&#xff0c;建立类库&#xff08;DLL&#xff09;。 (2)导入类库 在项目中建立文件夹DLL&#xff0c;直接把外部DLL和XML文件拖入文件夹&#xff0c…

input获得焦点和失去焦点

总结&#xff1a;placeholder因为在IE7 8 9 浏览器不支持所以没用它效果&#xff1a;当input获取光标的时候如果是默认提示则input内容为空。如果不是则为输入内容 当失去光标的时候&#xff0c;如果input内容为空&#xff0c;则恢复默认提示&#xff0c;如果不是则为…

带有DIY的Openshift上的Spring Boot / Java 8 / Tomcat 8

DIY墨盒是一种实验性墨盒&#xff0c;它提供了一种在OpenShift上测试不受支持的语言的方法。 它提供了最小限度的自由形式的支架&#xff0c;将墨盒的所有细节留给了应用程序开发人员 。 这篇博客文章说明了结合了PostgreSQL服务的Spring Boot / Java 8 / Tomcat 8应用程序的使…

zabbix监控docker容器

1、环境说明 由于最近zabbix进行过一次迁移&#xff0c;所以zabbix-server系列采用docker方式安装&#xff0c;参考zabbix官网&#xff1a;https://github.com/zabbix/zabbix-docker。为适应本地环境和需求&#xff0c;docker-compose.yml文件有改动&#xff0c;具体内容如下&a…

全角半角转换

#region 全角半角转换 /// <summary> /// 转全角的函数(SBC case) /// </summary> /// <param name"input">任意字符串</param> /// <returns>全角字符串</returns> ///<remarks> ///全角空格为12288&#xff0c;半角空格…

jquery中的left和top

left 和 top /*1. 获取元素基于定位容器的位置*//*返回的是对象 属性 left top */var position $(.inner).position();console.log(position.left);console.log(position.top);/*2. 获取元素基于页面容器的位置 基于页面的偏移*/var offset $(.inner).offset();console.l…

让我们用jOOλ在Java 8中流式传输地图

我想找到一种简单的方法来用Java 8流式传输Map 。猜猜是什么&#xff1f; 没有&#xff01; 为了方便起见&#xff0c;我期望的是以下方法&#xff1a; public interface Map<K, V> {default Stream<Entry<K, V>> stream() {return entrySet().stream();} …

双向链表的快速排序

#include <stdio.h> #include <stdlib.h> #include <time.h> //定义类型 所有的排序例子中都是用的int作为data typedef int elemType; //返回值 #define RET_SUCCESS ( 1 ) #define RET_FAILED ( 0 ) //定义链表的长度 #define LIST_MAX_SIZ…

Hibernate应用程序级可重复读取

介绍 在我以前的文章中&#xff0c;我描述了应用程序级事务如何为长时间的对话提供合适的并发控制机制。 所有实体都在Hibernate会话的上下文中加载&#xff0c;充当事务后写式缓存 。 Hibernate持久性上下文可以包含给定实体的一个引用和一个引用。 一级缓存可确保会话级可重…

HDU-2732 Leapin' Lizards 最大流

题目意思是有一些蜥蜴在一个迷宫里面&#xff0c;求这些蜥蜴还有多少是无论如何都逃不出来的。题目只给定一个行数&#xff0c;一个最远能够跳跃的距离&#xff0c;列数是不确定的&#xff08;题目告知列数小于等于20&#xff09;&#xff0c;但是数据一定会是一个矩阵。每只蜥…

canvas动画简单操作

canvas动画 小球滚动效果 关键api&#xff1a; window.requestAnimationFrame(draw) 会递归调用draw函数&#xff0c;替代setIntervalvar x 20; var speed 4; //电脑的帧率是1秒钟60Hz&#xff0c; 就相当于一秒钟可以播放60张图片&#xff0c;就相当于播放一张图片使用16.…

编译相关

编译的过程 &#xff1a; 1. 词法分析&#xff0c;程序被扫描成&#xff1a;关键字&#xff0c;标识符&#xff0c;字面量和特殊符号这些记号。 2. 语法分析&#xff0c;对记号进行语法分析产生语法树。&#xff08;代码最终由表达式组合&#xff09; 3. 语义分析&#xff0c;经…

带有Swagger的Spring Rest API –创建文档

使REST API易于使用的真正关键是好的文档。 但是&#xff0c;即使您的文档做得不错&#xff0c;您也需要设置公司流程的权利&#xff0c;以正确&#xff0c;及时地发布它。 确保利益相关者按时收到是一回事&#xff0c;但是您也要负责API和文档中的更新。 自动完成此过程可轻松…

关于React-native的介绍以及环境搭建

React-Native介绍&#xff08;后面内容的RN就是指react-native&#xff09; 由facebook公司推出的&#xff0c;基于react&#xff0c;能开发原生app 原理&#xff1a; 1. 利用react框架写好js代码 2. 利用python、C 把我们的js代码&#xff0c;翻译成java代码 3. 利用andr…

别说我不会玩,我来告诉你iPhone有多烂!iPhone缺点集

港行iPhone4S入手&#xff0c;帮朋友整的。朋友经常出国&#xff0c;所以指定要越狱&#xff0c;装墙外软件。 大sim卡是不支持的&#xff0c;还好商家给了剪卡器。首先发现移动卡不支持&#xff0c;别告诉我欢迎门&#xff0c;我用的就是19位的ICCID&#xff0c;反正把卡装进去…

使用PrimeFaces开发数据导出实用程序

我的日常工作涉及大量使用数据。 我们使用关系数据库来存储所有内容&#xff0c;因为我们依赖于企业级的数据管理。 有时&#xff0c;具有将数据提取为简单格式&#xff08;例如电子表格&#xff09;的功能很有用&#xff0c;以便我们可以按需进行操作。 这篇文章概述了我使用P…

函数名和变量名重名问题

1.如果输出一个函数名&#xff0c;那么输出的就是这个函数的代码。 function test(){console.log("哈哈");}console.log(test); 2.如果变量名 和函数名 重名会怎么样&#xff1f; //a.如果变量有值&#xff0c;那么输出的就是变量的值。//b.如果变量没有值&#xff…

css3 transition的应用

css3 的transition&#xff1a;允许css属性值在一定的时间区内平滑的过渡。 transition-property&#xff1a;变换延续的时间 transition-duration&#xff1a;在延续时间段 transition-timing-function&#xff1a;变换速度变化 transition-delay&#xff1a;变化延迟时间 a&a…

break和continue的区别

break 1.break 语句可用于跳出循环。 2.break所在的循环体已经结束。 continue 1.continue 语句中断循环中的迭代&#xff0c;如果出现了指定的条件&#xff0c;然后继续循环中的下一个迭代。 2.continue所在的循环体并没有结束。 demo演示 for (i0;i<5;i ){if (i3) br…

Tomcat到Wildfly:配置数据库连接

此摘录摘自《 从Tomcat到WildFly 》一书&#xff0c;您将在其中学习如何将现有的Tomcat体系结构移植到WildFly&#xff0c;包括服务器配置和在其顶部运行的应用程序。 WildFly是完全兼容的Java Enterprise Edition 7容器&#xff0c;与Tomcat相比&#xff0c;它具有更多的可用…