如何将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,一经查实,立即删除!

相关文章

zabbix监控docker容器

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

Hibernate应用程序级可重复读取

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

canvas动画简单操作

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

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

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

Tomcat到Wildfly:配置数据库连接

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

在jOOQ之上构建的RESTful JDBC HTTP服务器

jOOQ生态系统和社区正在持续增长。 我们个人总是很高兴看到基于jOOQ构建的其他开源项目。 今天&#xff0c;我们非常高兴为您介绍BjrnHarrtell结合REST和RDBMS的一种非常有趣的方法。 BjrnHarrtell从小就是瑞典的程序员。 他通常在Sweco Position AB上忙于编写GIS系统和集成&a…

node.js 搭建http调取 mysql数据库中的值

首先&#xff0c;我们先在数据库中创建两个表t_news,t_news_type;插入数据&#xff1a; 然后我们再写代码&#xff1a; //加载模块express var express require("express"); var fs require("fs"); //加载路径 var url require("url"); //加…

NHibernate.3.0.Cookbook第三章第9节的翻译

Using stateless sessions 使用无状态会话 当进行大量数据处理的时候,可能会放弃使用一些高级特性,而使用更接近底层的API来提高性能.在NHibernate中,这种高性能的底层API就是无状态的会话.本节介绍如何使用无状态会话来更新movie对象的价格. 准备 使用第一章的Eg.Core和第二章…

致电以验证您的JavaFX UI的响应能力

最近&#xff0c;Jim Weaver在他的Surface Pro上为演示安装了我的小图片索引应用“ picmodo”&#xff0c;并且GUI变成了垃圾。 显然&#xff0c;Windows Tablet上JavaFX的基本字体大小很高&#xff1a; 我认为&#xff0c;即使调整大小行为和预期一样工作&#xff0c;UI也绝…

vuex 管理vue-router的传值

假设有这样的一种情况&#xff0c;在两个组件中。一个组件【A】主要是比如说放表格数据&#xff0c;而另外一个组件【B】是专门用来向组件A的表格添加数据的表单。这个时候就是两个兄弟组件之间传递数据了。首先想到的是使用兄弟组件传递数据的方法&#xff1a; 新建一个中间件…

c++ 错误处理

void __fastcall TForm1::Button1Click(TObject *Sender) { try { int iEdit1->Text.ToInt(); Edit1->TextAnsiString(10/i); } catch (...) { ShowMessage("Error"); } } 通过 为知笔记 发布转载于:https://www.cnblogs.com/xe2011/archive/2012/07/16/55298e…

前后端分手大师——MVVM 模式

阅读目录简而言之组成部分没有什么是一个栗子不能解决的简而言之 之前对 MVVM 模式一直只是模模糊糊的认识&#xff0c;正所谓没有实践就没有发言权&#xff0c;通过这两年对 Vue 框架的深入学习和项目实践&#xff0c;终于可以装B了有了拨开云雾见月明的感觉。 Model–View–…

Java性能调优调查结果(第三部分)

这是该系列文章的第三篇&#xff0c;我们将分析2014年10月进行的调查的结果。如果您尚未这样做&#xff0c;我建议从该系列的前两篇文章开始&#xff1a; 问题严重性分析和监视域分析 。 这篇文章着重于故障排除/根本原因检测。 本调查部分的背景&#xff1a;意识到性能问题并…

划分树

昨天的杭电多校联合训练热身赛的一道题&#xff0c;求区间的中位数&#xff0c;快排会超时&#xff0c;划分树的模版题。。 划分树是一种基于线段树的数据结构。主要用于快速求出(在log(n)的时间复杂度内&#xff09;序列区间的第k大值 。划分树和归并树都是用线段树作为辅助的…

Canvas事件绑定

canvas事件绑定 众所周知canvas是位图&#xff0c;在位图里我们可以在里面画各种东西&#xff0c;可以是图片&#xff0c;可以是线条等等。那我们想给canvas里的某一张图片添加一个点击事件该怎么做到。而js只能监听到canvas的事件&#xff0c;很明显这个图片是不存在与dom里面…

控件注册 - 利用资源文件将dll、ocx打包进exe文件(转)

很多时候自定义或者引用控件都需要注册才能使用&#xff0c;但是如何使要注册的dll或ocx打包到exe中&#xff0c;使用户下载以后看到的只是一个exe,点击直接运行呢&#xff1f;就像很多安全控件&#xff0c;如支付宝的aliedit.exe那样。 现在介绍一种使用资源文件&#xff0c;将…

Canvas-图片旋转

Canvas-图片旋转 众所周知canvas是位图&#xff0c;你可以在里面渲染你要的东西&#xff0c;不过你只能操作canvas的属性来进行编辑。就是说你并不能操作画进canvas的东西&#xff0c;例如我在canvas里添加一幅画&#xff0c;我现在想将那幅画移动10px&#xff0c;我们并不能直…

CSS3实现多页签图片缩放切换效果

多页签切换效果&#xff0c;图片缩放&#xff0c;鼠标移动到图片上后显示文字内容等等&#xff0c;效果很集中呐 截图如下&#xff1a; 下载地址&#xff1a;http://www.lanrenzhijia.com/js/css3/2012/0718/602.html 预览地址&#xff1a;http://www.lanrenzhijia.com/yulan/2…

Angular使用总结 --- 如何正确的操作DOM

无奈接手了一个旧项目&#xff0c;上一个老哥在Angular项目中大量使用了JQuery来操作DOM&#xff0c;真的是太不讲究了。那么如何优雅的使用Angular的方式来操作DOM呢&#xff1f; 获取元素 1、ElementRef --- A wrapper around a native element inside of a View. 在组件…

非首屏图片延时加载

目标 减少资源加载可以明显的优化页面加载的速度&#xff0c;所以可以减少页面载入时立即下载的图片的数量&#xff0c;以提高页面加载速度&#xff0c;其他的图片在需要的时候再进行加载。 思路 想要实现以上的目标&#xff0c;有几个地方需要思考。 1、如何判断哪些图片需要…