日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

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

發布時間:2023/12/3 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如何将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(...));

現在開始有趣的部分。 我們如何指定我們要同時按Az和Aw分組? 我們需要為該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記錄,而是匯總x和y的各個值。 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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。