java关闭窗口函数_2016年将是Java终于拥有窗口函数的那一年!
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λ已经提供:
- 元组类型
- 对于有序的,仅顺序的流更有用的东西
通过最近发布的jOOλ0.9.9,我们添加了两个主要新功能:
- 大量新收藏家
- 视窗功能
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 |
+----+----+----+---------+---------+----------+
现在,您的分析心脏应该跳了起来。
等一会儿。 我们也可以像在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 BY
,ORDER 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
java关闭窗口函数
java关闭窗口函数_2016年将是Java终于拥有窗口函数的那一年!相关推荐
- 2016年将是Java终于拥有窗口函数的那一年!
你没听错. 到目前为止,出色的窗口功能是SQL独有的功能. 即使是复杂的函数式编程语言似乎也缺少这种漂亮的功能(如果我记错了,请纠正我,Haskell伙计们). 我们撰写了许多有关窗口函数的博客文章, ...
- java关闭安全警告_如何禁用Java“安全警告”弹出窗口?
将文本文档放在名为deployment.properties c:\ windows \ sun \ Java \ deployment \ deployment.properties的位置 尽管Ja ...
- 2016 java二级试题_2016计算机二级考试Java试题及答案
2016计算机二级考试Java试题及答案 (26)下面程序需要对Employee的对象进行存储,请在划线处填入正确答案. Class Employee implements________{ -} A ...
- java 关闭 tomcat,每次关闭TOMCAT就报错 不知道什么?该如何处理
当前位置:我的异常网» Java Web开发 » 每次关闭TOMCAT就报错 不知道什么?该如何处理 每次关闭TOMCAT就报错 不知道什么?该如何处理 www.myexceptions.net 网 ...
- java关闭applet_java – Applet会自动关闭
我的 java应用程序发生了非常奇怪的事情.总之,问题是它有时会在30-60秒的工作后自行关闭. 具体情况如下: >该应用程序实际上是在applet设置中启动的,applet加载主应用程序jar ...
- linux关闭的时候出现异常: java.net.ConnectException: 拒绝连接 (Connection refused)
linux关闭的时候出现异常: java.net.ConnectException: 拒绝连接 (Connection refused) 参考文章: (1)linux关闭的时候出现异常: java.n ...
- Java关闭挂钩– Runtime.addShutdownHook()
Java shutdown hook are handy to run some code when program exit. We can use java.lang.Runtime.addShu ...
- java关闭tomcat服务器端口_关闭tomcat端口号
一. CentOS 关闭tomcat端口号 1. 首先保证liunx下 ps -ef | grep java 2. 会显示如下信息 我使用的是IDEA打包的war包.tomcat是自带的 3. 查看未 ...
- java关闭inputstream_Java:浅谈InputStream的close方法
原则:最好在任何时候使用InputStream或者OutputStream的时候,在finally中调用close()方法,显式关闭. 一个典型的示例 InputStream in = null; t ...
最新文章
- 如何看懂一个深度学习的项目代码
- 2015下半年软考系统集成管理工程师10月8日作业
- margin-top失效的解决方法
- jQuery获取Table某列的值
- FastLeaderElection
- Chromium 内核新款 Edge 浏览器对比评测,微软找回面子全靠它了
- 16进制颜色与UIColor互转
- javascript中Promise深入理解(一)
- webpack 代码压缩优化篇
- Calib 工具说明
- cad卸载不干净_Mac软件卸载不干净?你可以试试AppCleaner
- 抖音开放平台开发记录
- LBS(基于位置服务)常见定位技术综合分析
- Tibco Designer -- 构建EAR文件
- Linux内核源代码概述
- VS code 安装Source Code Pro字体
- electron-updater更新遇到的问题 The URL protocol of the current origin (‘app://.‘) is not supported
- 动力电池:车企们的新角斗场
- input框中的value值到底是什么
- 一个按钮控制灯的开关