2016年将是Java终于拥有窗口函数的那一年!

你没听错。 到目前为止,出色的窗口功能是SQL独有的功能。 即使是复杂的函数式编程语言似乎也缺少这种漂亮的功能(如果我记错了,请纠正我,Haskell伙计们)。

我们撰写了许多有关窗口函数的博客文章,并在诸如以下文章中向我们的受众宣讲:

  • 可能最酷的SQL功能:窗口函数
  • 使用此整齐的窗口函数技巧来计算时间序列中的时差
  • 如何在SQL中找到最长的连续事件系列
  • 不要错过具有FIRST_VALUE(),LAST_VALUE(),LEAD()和LAG()的超凡SQL能力
  • ROW_NUMBER(),RANK()和DENSE_RANK()之间的区别

我最喜欢的窗口函数示例用例之一是运行总计 。 即从以下银行帐户交易表中获取:

| ID   | VALUE_DATE | AMOUNT |
|------|------------|--------|
| 9997 | 2014-03-18 |  99.17 |
| 9981 | 2014-03-16 |  71.44 |
| 9979 | 2014-03-16 | -94.60 |
| 9977 | 2014-03-16 |  -6.96 |
| 9971 | 2014-03-15 | -65.95 |

…到此,并计算出余额:

| ID   | VALUE_DATE | AMOUNT |  BALANCE |
|------|------------|--------|----------|
| 9997 | 2014-03-18 |  99.17 | 19985.81 |
| 9981 | 2014-03-16 |  71.44 | 19886.64 |
| 9979 | 2014-03-16 | -94.60 | 19815.20 |
| 9977 | 2014-03-16 |  -6.96 | 19909.80 |
| 9971 | 2014-03-15 | -65.95 | 19916.76 |

对于SQL,这是小菜一碟。 观察SUM(t.amount) OVER(...)的用法:

SELECTt.*,t.current_balance - NVL(SUM(t.amount) OVER (PARTITION BY t.account_idORDER BY     t.value_date DESC,t.id         DESCROWS BETWEEN UNBOUNDED PRECEDINGAND     1         PRECEDING),0) AS balance
FROM     v_transactions t
WHERE    t.account_id = 1
ORDER BY t.value_date DESC,t.id         DESC

窗口功能如何工作?

(别忘了预订我们的SQL Masterclass来了解窗口函数 ,以及更多!)

尽管有时语法有些令人恐惧,但窗口函数确实非常易于理解。 Windows是您的FROM / WHERE / GROUP BY / HAVING子句中产生的数据的“视图”。 它们使您可以访问相对于当前行的所有其他行,同时您可以在SELECT子句中(或者很少在ORDER BY子句中)进行计算。 上面的声明实际上是这样的:

| ID   | VALUE_DATE |  AMOUNT |  BALANCE |
|------|------------|---------|----------|
| 9997 | 2014-03-18 | -(99.17)|+19985.81 |
| 9981 | 2014-03-16 | -(71.44)| 19886.64 |
| 9979 | 2014-03-16 |-(-94.60)| 19815.20 |
| 9977 | 2014-03-16 |   -6.96 |=19909.80 |
| 9971 | 2014-03-15 |  -65.95 | 19916.76 |

也就是说,对于任何给定的余额,从当前余额中减去SUM()OVER() ”与当前行(同一银行帐户)在同一分区中的所有行的窗口,且这些行严格位于“高于”当前行。

或者,详细而言:

  • PARTITION BY指定“ OVER() ”,该字符将窗口范围排成一行
  • ORDER BY指定窗口的排序方式
  • ROWS指定应考虑的有序行索引

我们可以使用Java集合吗?

乔·布莱克 我们可以! 如果您使用的是jOOλ :我们设计了一个完全免费的开源Apache 2.0许可库,因为我们认为JDK 8 Stream和Collector API只是不这样做。

设计Java 8时,很多精力都放在了支持并行流上。 很好,但是当然不是可以应用函数式编程的唯一有用领域。 我们创建了jOOλ来填补这一空白-无需实现所有新的替代集合API,例如Javaslang或功能性Java have。

jOOλ已经提供:

  1. 元组类型
  2. 有序的,仅顺序的流更有用的东西

通过最近发布的jOOλ0.9.9,我们添加了两个主要新功能:

  1. 大量新收藏家
  2. 视窗功能

JDK中许多缺少的收集器

JDK附带了几个收集器,但是它们看起来确实笨拙且冗长,而且没有人真正喜欢编写像此Stack Overflow问题 (以及许多其他问题)中所介绍的收集器那样的收集器。

但是链接问题中公开的用例是非常有效的。 您要汇总人员列表中的几件事:

public class Person {private String firstName;private String lastName;private int age;private double height;private double weight;// getters / setters

假设您有以下列表:

List<Person> personsList = new ArrayList<Person>();personsList.add(new Person("John", "Doe", 25, 1.80, 80));
personsList.add(new Person("Jane", "Doe", 30, 1.69, 60));
personsList.add(new Person("John", "Smith", 35, 174, 70));

现在,您希望获得以下聚合:

  • 人数
  • 最高年龄
  • 最小高度
  • 平均重量

对于习惯于编写SQL的任何人来说,这都是一个荒谬的问题:

SELECT count(*), max(age), min(height), avg(weight)
FROM person

做完了 Java有多难? 事实证明,许多原始代码需要使用香草JDK 8 API编写。 考虑给出的复杂答案

  • 由塔吉尔·瓦列夫(Tagir Valeev)
  • 通过TriCore

使用jOOλ0.9.9时,再次解决此问题变得非常可笑, 并且读取的内容几乎类似于SQL :

Tuple result =
Seq.seq(personsList).collect(count(),max(Person::getAge),min(Person::getHeight),avg(Person::getWeight));System.out.println(result);

结果产生:

(3, Optional[35], Optional[1.69], Optional[70.0])

请注意,这并不针对SQL数据库运行查询(这就是jOOQ的目的)。 我们正在针对内存中的Java集合运行此“查询”。

现在窗口功能如何?

是的,本文的标题并没有涉及琐碎的聚合工作。 它承诺了很棒的窗口功能。

但是,窗口函数不过是数据流子集上的聚合(或排名)而已。 您想要维护原始记录,而不是将所有流(或表)聚合到单个记录中,而是直接在每个单独的记录上提供聚合。

窗口函数的一个很好的入门示例是本文提供的示例,它解释了ROW_NUMBER(),RANK()和DENSE_RANK()之间的区别 。 考虑以下PostgreSQL查询:

SELECTv, ROW_NUMBER() OVER(w),RANK()       OVER(w),DENSE_RANK() OVER(w)
FROM (VALUES('a'),('a'),('a'),('b'),('c'),('c'),('d'),('e')
) t(v)
WINDOW w AS (ORDER BY v);

它产生:

| V | ROW_NUMBER | RANK | DENSE_RANK |
|---|------------|------|------------|
| a |          1 |    1 |          1 |
| a |          2 |    1 |          1 |
| a |          3 |    1 |          1 |
| b |          4 |    4 |          2 |
| c |          5 |    5 |          3 |
| c |          6 |    5 |          3 |
| d |          7 |    7 |          4 |
| e |          8 |    8 |          5 |

在Java 8中,可以使用jOOλ0.9.9进行相同的操作

System.out.println(Seq.of("a", "a", "a", "b", "c", "c", "d", "e").window(naturalOrder()).map(w -> tuple(w.value(),w.rowNumber(),w.rank(),w.denseRank())).format()
);

屈服…

+----+----+----+----+
| v0 | v1 | v2 | v3 |
+----+----+----+----+
| a  |  0 |  0 |  0 |
| a  |  1 |  0 |  0 |
| a  |  2 |  0 |  0 |
| b  |  3 |  3 |  1 |
| c  |  4 |  4 |  2 |
| c  |  5 |  4 |  2 |
| d  |  6 |  6 |  3 |
| e  |  7 |  7 |  4 |
+----+----+----+----+

同样,请注意,我们没有对数据库运行任何查询。 一切都在内存中完成。

注意两件事:

  • jOOλ的窗口函数返回0(基于Java API的预期),而不是SQL(均基于1)。
  • 在Java中,无法使用命名列构造临时记录。 不幸的是,我确实希望将来的Java将为此类语言功能提供支持。

让我们回顾一下代码中到底发生了什么:

System.out.println(// This is just enumerating our valuesSeq.of("a", "a", "a", "b", "c", "c", "d", "e")// Here, we specify a single window to be// ordered by the value T in the stream, in// natural order.window(naturalOrder())// The above window clause produces a Window<T>// object (the w here), which exposes....map(w -> tuple(// ... the current value itself, of type String...w.value(),// ... or various rankings or aggregations on// the above window.w.rowNumber(),w.rank(),w.denseRank()))// Just some nice formatting to produce the table.format()
);

而已! 很简单,不是吗?

我们可以做的更多! 看一下这个:

System.out.println(Seq.of("a", "a", "a", "b", "c", "c", "d", "e").window(naturalOrder()).map(w -> tuple(w.value(),   // v0 w.count(),   // v1w.median(),  // v2w.lead(),    // v3w.lag(),     // v4w.toString() // v5)).format()
);

以上产量是多少?

+----+----+----+---------+---------+----------+
| v0 | v1 | v2 | v3      | v4      | v5       |
+----+----+----+---------+---------+----------+
| a  |  1 | a  | a       | {empty} | a        |
| a  |  2 | a  | a       | a       | aa       |
| a  |  3 | a  | b       | a       | aaa      |
| b  |  4 | a  | c       | a       | aaab     |
| c  |  5 | a  | c       | b       | aaabc    |
| c  |  6 | a  | d       | c       | aaabcc   |
| d  |  7 | b  | e       | c       | aaabccd  |
| e  |  8 | b  | {empty} | d       | aaabccde |
+----+----+----+---------+---------+----------+

现在,您的分析心脏应该跳了起来。

43765651

等一下。 我们也可以像在SQL中那样做框架吗? 我们可以。 就像在SQL中一样,当我们省略窗口定义上的frame子句(但我们确实指定了ORDER BY子句)时,默认情况下将应用以下内容:

RANGE BETWEEN UNBOUNDED PRECEDINGAND CURRENT ROW

我们在前面的示例中已经做到了。 可以在第v5列中看到,在该列中我们从第一个值一直到当前值聚合字符串。 因此,让我们指定框架:

System.out.println(Seq.of("a", "a", "a", "b", "c", "c", "d", "e").window(naturalOrder(), -1, 1) // frame here.map(w -> tuple(w.value(),   // v0w.count(),   // v1w.median(),  // v2w.lead(),    // v3w.lag(),     // v4w.toString() // v5)).format()
);

结果很简单:

+----+----+----+---------+---------+-----+
| v0 | v1 | v2 | v3      | v4      | v5  |
+----+----+----+---------+---------+-----+
| a  |  2 | a  | a       | {empty} | aa  |
| a  |  3 | a  | a       | a       | aaa |
| a  |  3 | a  | b       | a       | aab |
| b  |  3 | b  | c       | a       | abc |
| c  |  3 | c  | c       | b       | bcc |
| c  |  3 | c  | d       | c       | ccd |
| d  |  3 | d  | e       | c       | cde |
| e  |  2 | d  | {empty} | d       | de  |
+----+----+----+---------+---------+-----+

如预期的那样, lead()lag()不会受到影响,与count()median()toString()相反

现在,让我们回顾一下运行总计。

通常,您不会根据流本身的标量值来计算窗口函数,因为该值通常不是标量值,而是元组(或Java语言中的POJO)。 取而代之的是,您从元组(或POJO)中提取值并对其进行汇总。 因此,再次,在计算BALANCE ,我们需要首先提取AMOUNT

| ID   | VALUE_DATE |  AMOUNT |  BALANCE |
|------|------------|---------|----------|
| 9997 | 2014-03-18 | -(99.17)|+19985.81 |
| 9981 | 2014-03-16 | -(71.44)| 19886.64 |
| 9979 | 2014-03-16 |-(-94.60)| 19815.20 |
| 9977 | 2014-03-16 |   -6.96 |=19909.80 |
| 9971 | 2014-03-15 |  -65.95 | 19916.76 |

这是使用Java 8和jOOλ0.9.9编写运行总计的方法

BigDecimal currentBalance = new BigDecimal("19985.81");Seq.of(tuple(9997, "2014-03-18", new BigDecimal("99.17")),tuple(9981, "2014-03-16", new BigDecimal("71.44")),tuple(9979, "2014-03-16", new BigDecimal("-94.60")),tuple(9977, "2014-03-16", new BigDecimal("-6.96")),tuple(9971, "2014-03-15", new BigDecimal("-65.95")))
.window(Comparator.comparing((Tuple3<Integer, String, BigDecimal> t) -> t.v1, reverseOrder()).thenComparing(t -> t.v2), Long.MIN_VALUE, -1)
.map(w -> w.value().concat(currentBalance.subtract(w.sum(t -> t.v3).orElse(BigDecimal.ZERO))
));

屈服

+------+------------+--------+----------+
|   v0 | v1         |     v2 |       v3 |
+------+------------+--------+----------+
| 9997 | 2014-03-18 |  99.17 | 19985.81 |
| 9981 | 2014-03-16 |  71.44 | 19886.64 |
| 9979 | 2014-03-16 | -94.60 | 19815.20 |
| 9977 | 2014-03-16 |  -6.96 | 19909.80 |
| 9971 | 2014-03-15 | -65.95 | 19916.76 |
+------+------------+--------+----------+

这里有几件事发生了变化:

  • 比较器现在考虑两个比较。 不幸的是JEP-101并未完全实现 ,这就是为什么我们需要在此处帮助类型编译器的原因。
  • Window.value()现在是一个元组,而不是单个值。 因此,我们需要从中提取有趣的列AMOUNT (通过t -> t.v3 )。 另一方面,我们可以简单地将附加值concat()给元组

但是已经足够了。 除了比较器的详细信息(我们一定会在将来的jOOλ版本中解决)之外,编写窗口函数也是小菜一碟。

我们还能做什么?

本文不是对新API可以做的所有事情的完整描述。 我们将很快写一个后续博客文章,并附带其他示例。 例如:

  • 未描述partition by子句,但也可用
  • 您可以指定比此处公开的单个窗口更多的窗口,每个窗口都具有单独的PARTITION BYORDER BY和框架规范

另外,当前的实现还很规范,即,它还没有(但是)缓存聚合:

  • 对于无序/无框窗口(所有分区的值相同)
  • 严格递增的窗口(聚合可以基于先前的值,例如SUM()toString()关联收集器)

就我们而言就是这样。 下载jOOλ,试用它,并享受一个事实,那就是所有Java 8开发人员现在都可以使用最强大的SQL功能!

  • https://github.com/jOOQ/jOOL

翻译自: https://www.javacodegeeks.com/2016/01/2016-will-year-remembered-java-finally-window-functions.html

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

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

相关文章

android 仿京东地址选择_Android 开发:仿美团地址选择

最近做了这个功能&#xff0c;分享一下&#xff0c;用的是百度地图api&#xff0c;和美团外卖的地址选择界面差不多&#xff0c;也就是可以搜索或者滑动地图展示地址列表给用户选择&#xff0c;看下效果图先。文章重点展示地图并定位到“我”的位置 滑动地图获取周边poi(逆地理…

ps还原上一步快捷键_ps还原上一步快捷键_photoshop恢复上一步操作的快捷键是什么...

满意答案simonsinxer推荐于 2017.09.01采纳率&#xff1a;53% 等级&#xff1a;11已帮助&#xff1a;63469人还原/重做前一步操作 【Ctrl】【Z】其他一些快捷键&#xff1a;还原两步以上操作 【Ctrl】【Alt】【Z】重做两步以上操作 【Ctrl】【Shift】【Z】剪切选取的图像或路…

python中排序从小到大_从Python看排序:冒泡排序

冒泡排序在排序算法中是最简单的一种&#xff0c;它通过多次遍历列表&#xff0c;将最大的元素冒泡到列表的头部或尾部。我们通过对四张扑克牌&#xff08;花色相同&#xff09;以从小到大的方式进行排序来演示该算法的工作原理。首先将扑克牌面朝上放在桌上&#xff0c;如下图…

c语言三个数从小到大排序/输出_我的c语言笔记(三)

int表达式这个表达式存在的目的在于将表达式转为整数。比如&#xff1a;float a9999.9999&#xff1b;int b;b(int)(a/1000);就可以得到9啦&#xff0c;别忘了套上固定格式哦&#xff5e;然后我们接下来一起来做一道很重要的题哦&#xff0c;反复练习&#xff0c;可以顺利拿下同…

java虚拟_Java虚拟机(JVM)工作原理

虽然本教程的内容为 x86 处理器的原生汇编语言&#xff0c;但是了解其他机器架构如何工作也是有益的。JVM 是基于堆栈机器的首选示例。JVM 用堆栈实现数据传送、算术运算、比较和分支操作&#xff0c;而不是用寄存器来保存操作数(如同 x86 一样)。数据结构&#xff0c;让它们协…

java string blob_java String类型转换为Blob类型

展开全部这个是mysql下存取blob字段的一个很简单的类&#xff0c;跟据自己的需要32313133353236313431303231363533e4b893e5b19e31333332623936改改就行了/*** Title: BlobPros.java* Project: test* Description: 把图片存入mysql中的blob字段&#xff0c;并取出* Call Module…

Neo4j:特定关系与一般关系+属性

为了在Neo4j查询中获得最佳的遍历速度&#xff0c;我们应该使关系类型尽可能具体 。 让我们看一下几周前我在Skillsmatter上发表的“ 建模建议引擎建模 ”演讲中的一个例子。 我需要决定如何为成员和事件之间的“ RSVP”关系建模。 一个人可以对事件表示“是”或“否”&#…

java 按位置格式化字符串_Java字符串格式化,{}占位符根据名字替换实例

我就废话不多说了&#xff0c;大家还是直接看代码吧~import java.beans.PropertyDescriptor;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import java.util.regex.Matcher;import java.util.regex.Pattern;public class StringFormatUtil …

Apache Drill 1.4性能增强的简要概述

今天&#xff0c;我们很高兴宣布Apache Drill 1.4现已在MapR发行版中可用。 钻1.4是MAPR生产就绪和支持的版本&#xff0c;可以从下载这里 &#xff0c;找到1.4版本说明这里 。 Drill 1.4以其高度灵活和可扩展的体系结构为基础&#xff0c;带来了多种新功能以及对查询性能的增…

【01背包】洛谷P1282多米诺骨牌

题目描述 多米诺骨牌有上下2个方块组成&#xff0c;每个方块中有1~6个点。现有排成行的 上方块中点数之和记为S1&#xff0c;下方块中点数之和记为S2&#xff0c;它们的差为|S1-S2|。例如在图8-1中&#xff0c;S161119&#xff0c;S2153211&#xff0c;|S1-S2|2。每个多米诺骨牌…

java geolitecity_GeoLite2 Java根据IP获得城市、经纬度

之前我们介绍过通过 qqwry.dat 根据IP获得所属城市和运营商信息。但是这个 qqwry.dat 已经太久没更新了&#xff0c;数据有些不准确&#xff0c;而且现在我们有个需求就是想获取某个IP所在的经纬度。这里我们可以使用 GeoLite2&#xff0c;这个是国外开源的一个库&#xff0c;需…

计算机专业英语第二版张强华翻译_计算机语言发展的三个阶段,机器语言、汇编语言与高级语言...

在如今信息发达的时代&#xff0c;科技日新月异&#xff0c;计算机和Internet网络的发展也成为人们日常生活的重要部分。学习一两门计算机编程语言也如当初学习英文一样的火热&#xff0c;随着人工智能AI和云计算的不断发展&#xff0c;Python语言和Scala语言已经成为这两个领域…

使用Apache Drill REST API通过Node构建ASCII仪表板

Apache Drill有一个隐藏的瑰宝&#xff1a;易于使用的REST接口。 该API可用于查询&#xff0c;分析和配置Drill引擎。 在此博客文章中&#xff0c;我将说明如何使用Brilled Contrib使用Drill REST API创建ascii仪表板。 ASCII仪表盘如下所示&#xff1a; 先决条件 Node.js …

影子场vs.属性访问器接口第2轮

如果你们还没有注意到Dirk Lemmerman和我之间的&#xff08;轻松&#xff09; 对决 &#xff0c;那么让我快速提及一下我们是如何做到这一点的。 首先&#xff0c;Dirk创建了JavaFX技巧23&#xff1a;“ 为属性保存内存阴影字段 ”&#xff0c;以帮助应用程序开发人员在使用Jav…

Lowest Common Ancestor of a Binary Search Tree a Binary Tree

235. Lowest Common Ancestor of a Binary Search Tree 题目链接&#xff1a;https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/#/description 题目大意&#xff1a;给定一棵二叉查找树和两个节点p和q&#xff0c;要求返回这两个节点的第一个公共…

controller调用另一个controller中的方法 获取返回值_必须掌握!你知道 Spring 中运用的 9 种设计模式吗 ?...

Spring中涉及的设计模式总结&#xff0c;在面试中也会经常问道 Spring 中设计模式的问题。本文以实现方式、实质、实现原理的结构简单介绍 Sping 中应用的 9 种设计模型&#xff0c;具体详细的刨析会在后面的文章发布&#xff0c;话不多说&#xff0c;来个转发、在看、收藏三连…

我个人的CRUD故事-或我如何来到CUBA平台

在此博客文章中&#xff0c;我想介绍一下我如何使用CUBA平台以及此工具的好处。 在我年轻的“业务应用程序开发”历史上&#xff0c;我将深入探讨不同的阶段&#xff0c;只为您提供一些背景知识。 因此&#xff0c;让我们从如何进入典型的CRUD应用程序开始&#xff0c;以帮助非…

java 不定参数方法_java中不定长参数的使用方法

java中不定长参数的使用方法不定长参数方法的语法如下&#xff1a;返回值 方法名(参数类型...参数名称)在参数列表中使用“...”形式定义不定长参数&#xff0c;其实这个不定长参数a就是一个数组&#xff0c;编译器会将(int...a)这种形式看作是(int[] a)的形式。示例&#xff1…

光盘刻录只允许读取不能拷贝_原来 8 张图,就可以搞懂「零拷贝」了

作者 | 小林coding来源 | 小林coding(ID&#xff1a;CodingLin)前言磁盘可以说是计算机系统最慢的硬件之一&#xff0c;读写速度相差内存 10 倍以上&#xff0c;所以针对优化磁盘的技术非常的多&#xff0c;比如零拷贝、直接 I/O、异步 I/O 等等&#xff0c;这些优化的目的就是…

纯java分布式内存数据库_最新Java岗面试清单:分布式+Dubbo+线程+Redis+数据库+JVM+并发...

最近可能有点闲的慌&#xff0c;没事就去找面试面经&#xff0c;整理了一波面试题。我大概是分成了Java基础、中级、高级&#xff0c;分布式&#xff0c;Spring架构&#xff0c;多线程&#xff0c;网络&#xff0c;MySQL&#xff0c;Redis缓存&#xff0c;JVM相关&#xff0c;调…