sql limit 子句_Java 8流中的常见SQL子句及其等效项
sql limit 子句
功能編程允許使用通用語言進行準聲明性編程 。 通過使用功能強大的流暢API(例如Java 8的Stream API )或jOOλ的順序Stream擴展Seq或更復雜的庫(例如javaslang或functionaljava) ,我們可以以一種非常簡潔的方式來表示數據轉換算法。 比較相同算法的Mario Fusco的命令式和功能式版本 :
勢在必行–功能分離pic.twitter.com/G2cC6iBkDJ
— Mario Fusco(@mariofusco) 2015年3月1日
使用此類API,函數式編程肯定感覺就像是真正的聲明式編程。
最流行的真正的聲明式編程語言是SQL。 當聯接兩個表時,您不會告訴RDBMS如何實現該聯接。 它可以自行決定在完整查詢和所有可用元信息的上下文中,嵌套循環,合并聯接,哈希聯接或某種其他算法是否最合適。 這非常強大,因為對簡單連接有效的性能假設可能對復雜的連接不再有效,在復雜的連接上,其他算法的性能要優于原始算法。 通過這種抽象,您可以輕松地在30秒內修改查詢,而不必擔心諸如算法或性能之類的底層細節。
當一個API允許您將兩者結合起來(例如jOOQ和Streams )時,您將獲得兩個世界的精華-這些世界并沒有太大的不同。
在以下各節中,我們將比較常見SQL構造與使用Streams和jOOλ用Java 8編寫的等效表達式,以防Stream API沒有提供足夠的功能 。
元組
為了本文的方便,我們將假定SQL行/記錄在Java中具有等效的表示形式。 為此,我們將使用jOOλ的Tuple類型 ,該類型實質上是:
public class Tuple2<T1, T2> {public final T1 v1;public final T2 v2;public Tuple2(T1 v1, T2 v2) {this.v1 = v1;this.v2 = v2;} }…以及很多有用的頭,例如“ Comparable Tuple等。
請注意,在此示例和所有后續示例中,我們假定以下導入。
import static org.jooq.lambda.Seq.*; import static org.jooq.lambda.tuple.Tuple.*;import java.util.*; import java.util.function.*; import java.util.stream.*;import org.jooq.lambda.*;與SQL行很像,元組是“基于值”的類型 ,這意味著它實際上沒有標識。 兩個元組(1, 'A')和(1, 'A')可以被視為完全等效。 從游戲中刪除身份使具有不變數據結構SQL和函數式編程極為優雅。
FROM = of(),stream()等
在SQL中, FROM子句在邏輯上(但不是在語法上)位于所有其他子句之前。 它用于從至少一個表(可能是多個連接的表)生成一組元組。 例如,單表FROM子句可以簡單地映射到Stream.of() ,也可以映射到其他任何簡單地生成流的方法:
SQL
SELECT * FROM (VALUES(1, 1),(2, 2) ) t(v1, v2)屈服
+----+----+ | v1 | v2 | +----+----+ | 1 | 1 | | 2 | 2 | +----+----+Java
Stream.of(tuple(1, 1),tuple(2, 2) ).forEach(System.out::println);屈服
(1, 1) (2, 2)交叉聯接= flatMap()
從多個表中進行選擇已經更加有趣。 在SQL中合并兩個表的最簡單方法是通過表列表或使用CROSS JOIN生成笛卡爾積。 以下兩個是等效SQL語句:
SQL
-- Table list syntax SELECT * FROM (VALUES( 1 ), ( 2 )) t1(v1), (VALUES('A'), ('B')) t2(v2)-- CROSS JOIN syntax SELECT * FROM (VALUES( 1 ), ( 2 )) t1(v1) CROSS JOIN (VALUES('A'), ('B')) t2(v2)屈服
+----+----+ | v1 | v2 | +----+----+ | 1 | A | | 1 | B | | 2 | A | | 2 | B | +----+----+在交叉聯接(或笛卡爾乘積)中,將t1中的每個值與t2每個值組合在一起,總共產生size(t1) * size(t2)行。
Java
在使用Java 8的Stream函數編程時, Stream.flatMap()方法對應于SQL CROSS JOIN如以下示例所示:
List<Integer> s1 = Stream.of(1, 2); Supplier<Stream<String>> s2 = ()->Stream.of("A", "B");s1.flatMap(v1 -> s2.get().map(v2 -> tuple(v1, v2))).forEach(System.out::println);屈服
(1, A) (1, B) (2, A) (2, B)請注意我們必須將第二個流包裝在Supplier因為流只能被使用一次 ,但是上述算法實際上實現了嵌套循環,將流s2所有元素與流s1每個元素組合在一起。 另一種選擇是不使用流而是使用列表(為簡單起見,我們將在隨后的示例中進行操作):
List<Integer> s1 = Arrays.asList(1, 2); List<String> s2 = Arrays.asList("A", "B");s1.stream().flatMap(v1 -> s2.stream().map(v2 -> tuple(v1, v2))).forEach(System.out::println);實際上, CROSS JOIN可以在SQL和Java中輕松鏈接:
SQL
-- Table list syntax SELECT * FROM (VALUES( 1 ), ( 2 )) t1(v1), (VALUES('A'), ('B')) t2(v2), (VALUES('X'), ('Y')) t3(v3)-- CROSS JOIN syntax SELECT * FROM (VALUES( 1 ), ( 2 )) t1(v1) CROSS JOIN (VALUES('A'), ('B')) t2(v2) CROSS JOIN (VALUES('X'), ('Y')) t3(v3)屈服
+----+----+----+ | v1 | v2 | v3 | +----+----+----+ | 1 | A | X | | 1 | A | Y | | 1 | B | X | | 1 | B | Y | | 2 | A | X | | 2 | A | Y | | 2 | B | X | | 2 | B | Y | +----+----+----+Java
List<Integer> s1 = Arrays.asList(1, 2); List<String> s2 = Arrays.asList("A", "B"); List<String> s3 = Arrays.asList("X", "Y");s1.stream().flatMap(v1 -> s2.stream().map(v2 -> tuple(v1, v2))).flatMap(v12-> s3.stream().map(v3 -> tuple(v12.v1, v12.v2, v3))).forEach(System.out::println);屈服
(1, A, X) (1, A, Y) (1, B, X) (1, B, Y) (2, A, X) (2, A, Y) (2, B, X) (2, B, Y)注意我們如何從第一個CROSS JOIN操作中顯式取消嵌套元組,以在第二個操作中形成“扁平”元組。 當然,這是可選的。
Java與jOOλ的crossJoin()
我們jOOQ開發人員,我們是一個非常注重SQL的人員,因此為上述用例添加一個crossJoin()便捷方法是很自然的。 因此,我們的三重交叉聯接可以這樣寫:
Seq<Integer> s1 = Seq.of(1, 2); Seq<String> s2 = Seq.of("A", "B"); Seq<String> s3 = Seq.of("X", "Y");s1.crossJoin(s2).crossJoin(s3).forEach(System.out::println);屈服
((1, A), X) ((1, A), Y) ((1, B), X) ((1, B), Y) ((2, A), X) ((2, A), Y) ((2, B), X) ((2, B), Y)在這種情況下,我們并沒有嵌套在第一個交叉聯接中產生的元組。 僅從關系的角度來看,這都不重要。 嵌套元組與平面元組相同。 在SQL中,我們只是看不到嵌套。 當然,我們仍然可以通過添加一個附加的映射來嵌套:
Seq<Integer> s1 = Seq.of(1, 2); Seq<String> s2 = Seq.of("A", "B"); Seq<String> s3 = Seq.of("X", "Y");s1.crossJoin(s2).crossJoin(s3).map(t -> tuple(t.v1.v1, t.v1.v2, t.v2)).forEach(System.out::println);再次屈服
(1, A, X) (1, A, Y) (1, B, X) (1, B, Y) (2, A, X) (2, A, Y) (2, B, X) (2, B, Y)(您可能已經注意到map()對應于SELECT ,稍后我們將再次看到)
內部聯接= flatMap()與filter()
SQL INNER JOIN本質上只是SQL CROSS JOIN語法糖,其謂詞可減少CROSS JOIN后的元組集。 在SQL中,以下兩種內部聯接方式是等效的:
SQL
-- Table list syntax SELECT * FROM (VALUES(1), (2)) t1(v1), (VALUES(1), (3)) t2(v2) WHERE t1.v1 = t2.v2-- INNER JOIN syntax SELECT * FROM (VALUES(1), (2)) t1(v1) INNER JOIN (VALUES(1), (3)) t2(v2) ON t1.v1 = t2.v2屈服
+----+----+ | v1 | v2 | +----+----+ | 1 | 1 | +----+----+(請注意,關鍵字INNER是可選的)。
因此,“ t1的值2和“ t2 ”中的值3被“扔掉”,因為它們會產生聯接謂詞為true的任何行。
可以很容易地表達相同的內容,而在Java中則更詳細
Java(低效率的解決方案!)
List<Integer> s1 = Arrays.asList(1, 2); List<Integer> s2 = Arrays.asList(1, 3);s1.stream().flatMap(v1 -> s2.stream().map(v2 -> tuple(v1, v2))).filter(t -> Objects.equals(t.v1, t.v2)).forEach(System.out::println);以上正確產生
(1, 1)但是要注意,在生產笛卡爾積之后,您將獲得此結果,這是每個DBA的噩夢! 如本文開頭所述,與聲明式編程不同,在函數式編程中,您指示程序嚴格執行指定的操作順序。 換一種說法:
在函數式編程中, 您可以定義查詢的確切“執行計劃” 。
在聲明式編程中, 優化器可能會重組您的“程序”
沒有優化器可以將上述方法轉換為效率更高的方法:
Java(效率更高)
List<Integer> s1 = Arrays.asList(1, 2); List<Integer> s2 = Arrays.asList(1, 3);s1.stream().flatMap(v1 -> s2.stream().filter(v2 -> Objects.equals(v1, v2)).map(v2 -> tuple(v1, v2))).forEach(System.out::println);以上還產生
(1, 1)請注意,聯接謂詞如何從“外部”流轉移到“內部”流,這是通過傳遞給flatMap()的函數產生的。
Java(最佳)
如前所述,函數式編程不一定允許您根據對實際數據的了解來重寫算法。 上面介紹的用于聯接的實現始終實現從第一個流到第二個流的嵌套循環聯接。 如果您加入了兩個以上的流,或者第二個流非常大,則此方法效率極低。 復雜的RDBMS絕不會像這樣盲目地應用嵌套循環聯接,而要在實際數據上考慮約束,索引和直方圖。
不過,深入探討該主題將超出本文的范圍。
Java與jOOλ的innerJoin()
再次,由于我們在jOOQ上的工作的啟發,我們還為上述用例添加了innerJoin()便捷方法:
Seq<Integer> s1 = Seq.of(1, 2); Seq<Integer> s2 = Seq.of(1, 3);s1.innerJoin(s2, (t, u) -> Objects.equals(t, u)).forEach(System.out::println);屈服
(1, 1)…因為畢竟,當連接兩個流時,唯一真正有趣的操作是join Predicate 。 所有其他內容(平面映射等)都只是樣板。
LEFT OUTER JOIN = flatMap(),帶有filter()和“ default”
SQL的OUTER JOIN工作方式與INNER JOIN ,不同之處在于,如果JOIN謂詞對成對的元組產生false ,則會生成其他“默認”行。 就集合論/關系代數而言 ,可以表示為:
或使用SQL風格的方言:
R LEFT OUTER JOIN S ::=R INNER JOIN S UNION ((R EXCEPT (SELECT R.* FROM R INNER JOIN S))CROSS JOIN(null, null, ..., null) )這只是意味著左外側接合時S到R會有在結果至少一行中的每一行R與可能的空值S 。
相反地,當右外接合 S到R會有在結果中的每一行的至少一行S ,與可能的空值R
最后,當完全外部接合 S到R會有在結果中的每一行的至少一行R與可能為空值S 和用于在每行S具有用于可能為空值R
讓我們看一下LEFT OUTER JOIN ,它是SQL中最常用的。
SQL
-- Table list, Oracle syntax (don't use this!) SELECT * FROM (SELECT 1 v1 FROM DUALUNION ALL SELECT 2 v1 FROM DUAL) t1, (SELECT 1 v2 FROM DUALUNION ALLSELECT 3 v2 FROM DUAL) t2 WHERE t1.v1 = t2.v2 (+)-- OUTER JOIN syntax SELECT * FROM (VALUES(1), (2)) t1(v1) LEFT OUTER JOIN (VALUES(1), (3)) t2(v2) ON t1.v1 = t2.v2屈服
+----+------+ | v1 | v2 | +----+------+ | 1 | 1 | | 2 | null | +----+------+(請注意,關鍵字OUTER是可選的)。
Java
不幸的是,如果流為空,JDK的Stream API不能為我們提供一種簡單的方法來從流中產生“至少”一個值。 我們可能正在編寫實用程序函數,如Stack Overflow上的Stuart Marks所述 :
static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Supplier<T> supplier) {Iterator<T> iterator = stream.iterator();if (iterator.hasNext()) {return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);} else {return Stream.of(supplier.get());} }或者,我們只使用jOOλ的Seq.onEmpty()
List<Integer> s1 = Arrays.asList(1, 2); List<Integer> s2 = Arrays.asList(1, 3);seq(s1) .flatMap(v1 -> seq(s2).filter(v2 -> Objects.equals(v1, v2)).onEmpty(null).map(v2 -> tuple(v1, v2))) .forEach(System.out::println);(注意,我們在流中添加null 。這可能并不總是一個好主意。我們將在以后的博客文章中對此進行跟進)
以上還產生
(1, 1) (2, null)如何讀取隱式左外部聯接?
- 我們將從左側流s1獲取每個值v1
 - 對于每個這樣的值v1 ,我們將右流s2平面化以生成元組(v1, v2) (笛卡爾乘積,交叉聯接)
 - 我們將對每個這樣的元組(v1, v2)應用連接謂詞
 - 如果join謂詞對任何值v2不留下任何元組,我們將生成一個包含左流v1的值和null的單個元組
 
帶有jOOλ的Java
為了方便起見,jOOλ還支持leftOuterJoin() ,其工作原理如上所述:
Seq<Integer> s1 = Seq.of(1, 2); Seq<Integer> s2 = Seq.of(1, 3);s1.leftOuterJoin(s2, (t, u) -> Objects.equals(t, u)).forEach(System.out::println);屈服
(1, 1) (2, null)右外連接=左外連接反向
通常, RIGHT OUTER JOIN只是前一個LEFT OUTER JOIN的逆。 rightOuterJoin()的jOOλ實現如下所示:
default <U> Seq<Tuple2<T, U>> rightOuterJoin(Stream<U> other, BiPredicate<T, U> predicate) {return seq(other).leftOuterJoin(this, (u, t) -> predicate.test(t, u)).map(t -> tuple(t.v2, t.v1)); }如您所見, RIGHT OUTER JOIN反轉了LEFT OUTER JOIN的結果,就是這樣。 例如:
Seq<Integer> s1 = Seq.of(1, 2); Seq<Integer> s2 = Seq.of(1, 3);s1.rightOuterJoin(s2, (t, u) -> Objects.equals(t, u)).forEach(System.out::println);屈服
(1, 1) (null, 3)在哪里= filter()
最簡單的映射可能是SQL的WHERE子句在Stream API中具有完全等效的內容: Stream.filter() 。
SQL
SELECT * FROM (VALUES(1), (2), (3)) t(v) WHERE v % 2 = 0屈服
+---+ | v | +---+ | 2 | +---+Java
Stream<Integer> s = Stream.of(1, 2, 3);s.filter(v -> v % 2 == 0).forEach(System.out::println);屈服
2通常, filter()和Stream API的有趣之處在于,該操作可以在調用鏈中的任何位置應用,這與WHERE子句不同, WHERE子句被限制在FROM子句之后–即使SQL的JOIN .. ON或HAVING子句在語義上相似。
GROUP BY = collect()
最不直接的映射是GROUP BY與Stream.collect() 。
首先, 要完全理解 SQL的GROUP BY可能有些棘手 。 它實際上是FROM子句的一部分,它將FROM .. JOIN .. WHERE生成的元組集轉換為元組組,其中每個組都有一個關聯的可聚合元組集,可以在HAVING , SELECT和ORDER BY子句。 當您使用諸如GROUPING SETS類的OLAP功能時,事情變得更加有趣,它可以根據幾種分組組合來復制元組。
在大多數不支持ARRAY或MULTISET SQL實現中,可聚合的元組在SELECT中不可用(即嵌套集合)。 在這里, Stream API的功能集非常出色。 另一方面, Stream API只能將值作為終端操作進行分組,其中在SQL中, GROUP BY是純粹以聲明方式(因此是惰性地)應用的。 如果不需要,執行計劃者可以選擇根本不執行GROUP BY 。 例如:
SELECT * FROM some_table WHERE EXISTS (SELECT x, sum(y)FROM other_tableGROUP BY x )上面的查詢在語義上等同于
SELECT * FROM some_table WHERE EXISTS (SELECT 1FROM other_table )子查詢中的分組是不必要的。 可能有人從其他位置將子查詢復制粘貼到該子查詢中,或者將查詢整體進行了重構。 在Java中,使用Stream API,總是執行每個操作。
為了簡單起見,我們將堅持最簡單的示例
沒有GROUP BY的匯總
一種特殊情況是當我們不指定任何GROUP BY子句時。 在這種情況下,我們可以在FROM子句的所有列上指定聚合,從而始終只生成一條記錄。 例如:
SQL
SELECT sum(v) FROM (VALUES(1), (2), (3)) t(v)屈服
+-----+ | sum | +-----+ | 6 | +-----+Java
Stream<Integer> s = Stream.of(1, 2, 3);int sum = s.collect(Collectors.summingInt(i -> i)); System.out.println(sum);屈服
6使用GROUP BY進行匯總
如前所述,在SQL中更常見的聚合情況是指定一個顯式的GROUP BY子句。 例如,我們可能要按偶數和奇數分組:
SQL
SELECT v % 2, count(v), sum(v) FROM (VALUES(1), (2), (3)) t(v) GROUP BY v % 2屈服
+-------+-------+-----+ | v % 2 | count | sum | +-------+-------+-----+ | 0 | 1 | 2 | | 1 | 2 | 4 | +-------+-------+-----+Java
幸運的是,對于這個簡單的分組/收集用例,JDK提供了一個稱為Collectors.groupingBy()的實用程序方法,該方法生成一個收集器,該收集器生成Map<K, List<V>>類型,如下所示:
Stream<Integer> s = Stream.of(1, 2, 3);Map<Integer, List<Integer>> map = s.collect(Collectors.groupingBy(v -> v % 2) );System.out.println(map);屈服
{0=[2], 1=[1, 3]}這當然會照顧到分組。 現在,我們要為每個組生成聚合。 有點尷尬的JDK方法是:
Stream<Integer> s = Stream.of(1, 2, 3);Map<Integer, IntSummaryStatistics> map = s.collect(Collectors.groupingBy(v -> v % 2,Collectors.summarizingInt(i -> i)) );System.out.println(map);我們現在將獲得:
{0=IntSummaryStatistics{count=1, sum=2, min=2, average=2.000000, max=2},1=IntSummaryStatistics{count=2, sum=4, min=1, average=2.000000, max=3}}如您所見, count()和sum()值是根據上述內容計算出來的。
更復雜的GROUP BY
當使用Java 8的Stream API進行多個聚合時,您將很快被迫與自己實現復雜的收集器和累加器的低級API進行角力。 這是乏味且不必要的。 考慮以下SQL語句:
SQL
CREATE TABLE t (w INT,x INT,y INT,z INT );SELECTz, w, MIN(x), MAX(x), AVG(x), MIN(y), MAX(y), AVG(y) FROM t GROUP BY z, w;我們希望一口氣:
- 按幾個值分組
 - 從多個值匯總
 
Java
在上一篇文章中,我們已經詳細解釋了如何使用jOOλ中的便捷API通過 Seq.groupBy()
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;} }Map<Tuple2<Integer, Integer>, Tuple2<IntSummaryStatistics, IntSummaryStatistics> > map = 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)) );System.out.println(map);以上收益
{(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})}有關更多詳細信息, 請在此處閱讀全文 。
請注意,使用Stream.collect()或Seq.groupBy()如何實現隱式SELECT子句,不再需要通過map()獲得(見下文)。
再次= filter()
如前所述,使用Stream API并沒有真正不同的方式來應用謂詞,只有Stream.filter() 。 在SQL中, HAVING是一個“特殊”謂詞子句,在語法上位于GROUP BY子句之后。 例如:
SQL
SELECT v % 2, count(v) FROM (VALUES(1), (2), (3)) t(v) GROUP BY v % 2 HAVING count(v) > 1屈服
+-------+-------+ | v % 2 | count | +-------+-------+ | 1 | 2 | +-------+-------+Java
不幸的是,正如我們之前所看到的, collect()是Stream API中的終端操作,這意味著它急切地生成Map ,而不是將Stream<T>轉換為Stream<K, Stream<V> ,在復雜的Stream組合更好。 這意味著我們要在收集后立即執行的任何操作都必須在從輸出Map生成的新流上實施:
Stream<Integer> s = Stream.of(1, 2, 3);s.collect(Collectors.groupingBy(v -> v % 2,Collectors.summarizingInt(i -> i))).entrySet().stream().filter(e -> e.getValue().getCount() > 1).forEach(System.out::println);屈服
1=IntSummaryStatistics{count=2, sum=4, min=1, average=2.000000, max=3}如您所見,應用的類型轉換為:
- Map<Integer, IntSummaryStatistics>
 - Set<Entry<Integer, IntSummaryStatistics>>
 - Stream<Entry<Integer, IntSummaryStatistics>>
 
選擇= map()
SQL中的SELECT子句不過是一個元組轉換函數,該函數采用FROM子句產生的元組的笛卡爾積,并將其轉換為新的元組表達式,然后將其饋送到客戶端或某些更高級別的查詢(如果有)這是一個嵌套的SELECT。 插圖:
從輸出
+------+------+------+------+------+ | T1.A | T1.B | T1.C | T2.A | T2.D | +------+------+------+------+------+ | 1 | A | a | 1 | X | | 1 | B | b | 1 | Y | | 2 | C | c | 2 | X | | 2 | D | d | 2 | Y | +------+------+------+------+------+應用選擇
SELECT t1.a, t1.c, t1.b || t1.d+------+------+--------------+ | T1.A | T1.C | T1.B || T1.D | +------+------+--------------+ | 1 | a | AX | | 1 | b | BY | | 2 | c | CX | | 2 | d | DY | +------+------+--------------+使用Java 8 Streams,可以使用Stream.map()非常簡單地實現SELECT ,正如我們在前面的示例中已經看到的那樣,其中我們使用map()取消了元組的嵌套。 以下示例在功能上等效:
SQL
SELECT t.v1 * 3, t.v2 + 5 FROM (VALUES(1, 1),(2, 2) ) t(v1, v2)屈服
+----+----+ | c1 | c2 | +----+----+ | 3 | 6 | | 6 | 7 | +----+----+Java
Stream.of(tuple(1, 1),tuple(2, 2) ).map(t -> tuple(t.v1 * 3, t.v2 + 5)).forEach(System.out::println);屈服
(3, 6) (6, 7)DISTINCT = distinct()
該DISTINCT可以與被提供的關鍵字SELECT從句簡單地移除它們已經被產生之后立即重復元組SELECT子句。 插圖:
從輸出
+------+------+------+------+------+ | T1.A | T1.B | T1.C | T2.A | T2.D | +------+------+------+------+------+ | 1 | A | a | 1 | X | | 1 | B | b | 1 | Y | | 2 | C | c | 2 | X | | 2 | D | d | 2 | Y | +------+------+------+------+------+應用SELECT DISTINCT
SELECT DISTINCT t1.a+------+ | T1.A | +------+ | 1 | | 2 | +------+使用Java 8 Streams,可以在Stream.distinct()之后Stream.map()使用Stream.distinct()來非常簡單地實現SELECT DISTINCT 。 以下示例在功能上等效:
SQL
SELECT DISTINCT t.v1 * 3, t.v2 + 5 FROM (VALUES(1, 1),(2, 2),(2, 2) ) t(v1, v2)屈服
+----+----+ | c1 | c2 | +----+----+ | 3 | 6 | | 6 | 7 | +----+----+Java
Stream.of(tuple(1, 1),tuple(2, 2),tuple(2, 2) ).map(t -> tuple(t.v1 * 3, t.v2 + 5)).distinct().forEach(System.out::println);屈服
(3, 6) (6, 7)UNION ALL = concat()
設置操作在SQL和使用Stream API中都非常強大。 UNION ALL操作映射到Stream.concat() ,如下所示:
SQL
SELECT * FROM (VALUES(1), (2)) t(v) UNION ALL SELECT * FROM (VALUES(1), (3)) t(v)屈服
+---+ | v | +---+ | 1 | | 2 | | 1 | | 3 | +---+Java
Stream<Integer> s1 = Stream.of(1, 2); Stream<Integer> s2 = Stream.of(1, 3);Stream.concat(s1, s2).forEach(System.out::println);屈服
1 2 1 3Java(使用jOOλ)
不幸的是, concat()僅作為static方法存在于Stream ,而使用Seq.concat()時, Seq.concat()也存在于實例上。
Seq<Integer> s1 = Seq.of(1, 2); Seq<Integer> s2 = Seq.of(1, 3);s1.concat(s2).forEach(System.out::println);UNION = concat()和distinct()
在SQL中,定義UNION以通過UNION ALL將兩個集合連接起來后刪除重復項。 以下兩個語句是等效的:
SELECT * FROM t UNION SELECT * FROM u;-- equivalentSELECT DISTINCT * FROM (SELECT * FROM tUNION ALLSELECT * FROM u );讓我們付諸行動:
SQL
SELECT * FROM (VALUES(1), (2)) t(v) UNION SELECT * FROM (VALUES(1), (3)) t(v)屈服
+---+ | v | +---+ | 1 | | 2 | | 3 | +---+Java
Stream<Integer> s1 = Stream.of(1, 2); Stream<Integer> s2 = Stream.of(1, 3);Stream.concat(s1, s2).distinct().forEach(System.out::println);ORDER BY = sorted()
ORDER BY映射很簡單
SQL
SELECT * FROM (VALUES(1), (4), (3)) t(v) ORDER BY v屈服
+---+ | v | +---+ | 1 | | 3 | | 4 | +---+Java
Stream<Integer> s = Stream.of(1, 4, 3);s.sorted().forEach(System.out::println);屈服
1 3 4LIMIT = limit()
LIMIT映射更加簡單
SQL
SELECT * FROM (VALUES(1), (4), (3)) t(v) LIMIT 2屈服
+---+ | v | +---+ | 1 | | 4 | +---+Java
Stream<Integer> s = Stream.of(1, 4, 3);s.limit(2).forEach(System.out::println);屈服
1 4偏移= skip()
OFFSET映射也很簡單
SQL
SELECT * FROM (VALUES(1), (4), (3)) t(v) OFFSET 1屈服
+---+ | v | +---+ | 4 | | 3 | +---+Java
Stream<Integer> s = Stream.of(1, 4, 3);s.skip(1).forEach(System.out::println);屈服
4 3結論
在上面的文章中,我們已經看到了幾乎所有有用SQL SELECT查詢子句,以及如何將它們映射到Java 8 Stream API或jOOλ的Seq API,以防Stream無法提供足夠的功能。
本文表明,SQL的聲明性世界與Java 8的功能性世界沒有太大不同。 SQL子句可以組成即席查詢,就像Stream方法可以用來組成功能轉換管道一樣。 但是有根本的區別。
盡管SQL確實是聲明性的,但是函數式編程還是很有啟發性的。 Stream API不會基于約束,索引,直方圖和有關要轉換的數據的其他元信息來做出優化決策。 使用Stream API就像在SQL中使用所有可能的優化提示一樣,以強制SQL引擎選擇一個特定的執行計劃而不是另一個。 但是,盡管SQL是更高級別的算法抽象,但是Stream API可能允許您實現更多可自定義的算法。
翻譯自: https://www.javacodegeeks.com/2015/08/common-sql-clauses-and-their-equivalents-in-java-8-streams.html
sql limit 子句
總結
以上是生活随笔為你收集整理的sql limit 子句_Java 8流中的常见SQL子句及其等效项的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 圆缺是什么 圆缺的解释
 - 下一篇: 身份证哪个是正面 身份证正面是哪一面