我无法抗拒。 我已经阅读了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(...));

现在开始有趣的部分。 我们如何指定我们要同时按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,届时上述所有内容都有望从新的值类型和泛型类型专门化中受益。

我们创建了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

如何将SQL GROUP BY和聚合转换为Java 8相关推荐

  1. sql group by 取每组符合条件_从零学SQL-经典面试题

    面试训练题根据数据库school来练习,其中四个表数据如下: 一.简单查询 面试官:查询姓"猴"的学生名单. 面试官:查询姓名中最后一个字是"猴"的学生名单. ...

  2. sql group by having用法_神奇的 SQL,Group By 真扎心,原来是这样!

    GROUP BY 后 SELECT 列的限制 标准 SQL 规定,在对表进行聚合查询的时候,只能在 SELECT 子句中写下面 3 种内容:通过 GROUP BY 子句指定的聚合键.聚合函数(SUM ...

  3. sql group by having用法_神奇的 SQL 为什么 GROUP BY 之后不能直接引用原表中的列?...

    作者:青石路 cnblogs.com/youzhibing/p/11516154.html GROUP BY 后 SELECT 列的限制 标准 SQL 规定,在对表进行聚合查询的时候,只能在 SELE ...

  4. sql用于字符串的聚合函数_SQL字符串函数用于数据整理(争用)

    sql用于字符串的聚合函数 In this article, you'll learn the tips for getting started using SQL string functions ...

  5. 第53章 SQL GROUP BY 语句教程

    GROUP BY 语句可结合一些聚合函数来使用 GROUP BY 语句 GROUP BY 语句用于结合聚合函数,根据一个或多个列对结果集进行分组. SQL GROUP BY 语法 SELECT col ...

  6. 【 jsqlparser学习】SQL转换为java类

    jsqlparser学习 一.主要开源API SqlParser是一个SQL语句解析器.它将SQL转换为Java类的可遍历层次结构. 1.guava下的graph包 graph包下的类,解决DAG矢量 ...

  7. SQL GROUP BY 语句

    合计函数 (比如 SUM) 常常需要添加 GROUP BY 语句. GROUP BY 语句 GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组. SQL GROUP BY 语法 ...

  8. 如何将java.util.Date转换为java.sql.Date?

    我试图使用java.util.Date作为输入,然后用它创建一个查询-所以我需要一个java.sql.Date . 我很惊讶地发现它不能隐式或显式地进行转换-但我什至不知道该怎么做,因为Java AP ...

  9. sql server 中将datetime类型转换为date,或者time

    sql server 中将datetime类型转换为date,或者time 2008年01月14日 星期一 14:46 这个转换总是记不住,用到的时候就找,现贴上来,以备查用. datetime类型转 ...

最新文章

  1. Hadoop学习之Mapreduce执行过程详解
  2. 【PP操作手册】运行MRP产生计划订单
  3. 九十三、动态规划系列之股票问题(下)
  4. 通过错误的sql来测试推理sql的解析过程
  5. sqlite java blob_【转】好东西!sqlite3中BLOB数据类型存储大对象运用示例
  6. Java中常用的测试工具JUnit
  7. html把实线变成虚线,Html5 Canvas 绘制虚线和实线的切换方法
  8. 关于String a=new String(a)创建几个对象问题的正确答案
  9. Python访问MySQL数据库速度慢解决方法
  10. linux系统镜像怎么安装,linux系统安装,怎样安装linux系统制作方法
  11. C语言数据结构之管道浅析
  12. 个人设计web前端大作业——HTML+CSS华为官网首页
  13. 优秀html5网页设计,五个国外优秀的HTML5酷站欣赏|H5开发第二课
  14. 【无标题】【3D建模制作技巧分享】zbrush中如何卡硬边?
  15. 高斯消元简单线性代数线性基学习记录
  16. 计算机人才供需状况和就业形势分析,计算机科学与技术就业形势分析
  17. Android图表控件MPAndroidChart——BarChart实现多列柱状图以及堆积柱状图
  18. 银行卡号定位(python3)
  19. android版怎么下载地址,负重前行怎么下载到手机 最新安卓版下载地址
  20. yolo论文_YOLO之父宣布退出CV界,坦言无法忽视自己工作带来的负面影响

热门文章

  1. 同步和异步有何异同,什么场景使用
  2. android菱形imageview,ios – 在UICollectionView中,UIImageView应该是圆形视图而不是菱形...
  3. centos Error: Cannot find a valid baseurl for repo: base 解决方法
  4. tomcat(8)载入器
  5. jcmd_jcmd,大约JDK 11
  6. java rop_Java命令行界面(第23部分):Rop
  7. hibernate乐观锁_Hibernate Collection乐观锁定
  8. input发送a.jax_Java EE 7 / JAX-RS 2.0 – REST上的CORS
  9. junit测试报告生成_这是东西:jUnit:动态测试生成
  10. 使用Zuul和Spring Boot创建API网关