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 ,则会生成其他“默认”行。 就集合论/关系代数而言 ,可以表示为:

dd81ee1373d922122ce1b3e0da74cb28

或使用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)
)

这只是意味着左外侧接合SR会有在结果至少一行中的每一行R与可能的空值S

相反地,当右外接合 SR会有在结果中的每一行的至少一行S ,与可能的空值R

最后,当完全外部接合 SR会有在结果中的每一行的至少一行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 .. ONHAVING子句在语义上相似。

GROUP BY = collect()

最不直接的映射是GROUP BYStream.collect()

首先, 要完全理解 SQL的GROUP BY可能有些棘手 。 它实际上是FROM子句的一部分,它将FROM .. JOIN .. WHERE生成的元组集转换为元组组,其中每个组都有一个关联的可聚合元组集,可以在HAVINGSELECTORDER BY子句。 当您使用诸如GROUPING SETS类的OLAP功能时,事情变得更加有趣,它可以根据几种分组组合来复制元组。

在大多数不支持ARRAYMULTISET 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
3

Java(使用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
4

LIMIT = 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 子句

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

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

相关文章

C++编程中的核心知识点!

点击蓝字关注我们来源于网络&#xff0c;侵删尊重函数接口&#xff0c;尽量不作内部改动C代码语句分为&#xff1a;内置类型&#xff0c;名字&#xff0c;变量&#xff0c;操作符&#xff0c;标量&#xff0c;字符串&#xff0c;预处理指示&#xff08;如#include&#xff09;等…

python3.70_Python3 基本数据类型

Python3 基本数据类型Python 中的变量不需要声明。每个变量在使用前都必须赋值&#xff0c;变量赋值以后该变量才会被创建。在 Python 中&#xff0c;变量就是变量&#xff0c;它没有类型&#xff0c;我们所说的"类型"是变量所指的内存中对象的类型。等号()用来给变量…

jdbc mysql驱动_MySQL JDBC驱动程序如何处理准备好的语句

jdbc mysql驱动准备的语句类型 在研究《 高性能Java持久性》一书中的“ 语句缓存”一章时&#xff0c;我有机会比较了Oracle&#xff0c;SQL Server&#xff0c;PostgreSQL和MySQL处理预准备语句的方式。 感谢Jess Balint &#xff08;MySQL JDBC驱动程序撰稿人&#xff09;在…

C++ 语言的单元测试与代码覆盖率

点击蓝字关注我们来源于网络&#xff0c;侵删前言测试是软件开发过程中一个必须的环节&#xff0c;测试确保软件的质量符合预期。对于工程师自己来说&#xff0c;单元测试也是提升自信心的一种方式。直接交付没有经过测试的代码是不太好的&#xff0c;因为这很可能会浪费整个团…

使用Spring AOP和番石榴速率限制器的节气门方法

外部服务或API可能有使用限制&#xff0c;或者它们不能失败就无法处理大量请求。 这篇文章解释了如何创建一个基于Spring Framework的方面&#xff0c;该方面可以用来限制使用Guava速率限制器的任何建议方法调用。 以下实现需要Java 8&#xff0c;Spring AOP和Guava。 让我们从…

C++ 模板(Template)总结,长点小知识

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删前言&#xff1a;大家好&#xff0c;今天给大家分享一篇关于 C 模板总结概述.模板&#xff08;Template&#xff09;指 C 程序设计设计语言中采用…

归并排序 java_归并排序(Java实现)

package primary_01;/** 归并排序* 1.算法实现* 2.验证算法的正确性* 3.分析算法的复杂度*/public class Merge_sort {public static void main(String[] args) {int arr [] {2,1,6,5,9,8,2020,199};sort(arr, 0, arr.length-1);for (int i : arr) {System.out.print(i" &…

python kotlin_在Python,Java和Kotlin中标记参数和重载

python kotlin在多种语言之间跳来跳去可以帮助您注意到不同语言的习惯用法和最佳做法之间的某些差异。 比较有趣的差异之一与一个函数执行多项操作有关。 Python 我们先来看一下Python。 Python实际上无法重载&#xff0c;因为用相同的名称定义一个新的函数/方法只会覆盖前一个…

python调用zabbixapi接口_python调用zabbix api获取主机信息

import requestsimport jsonheaders {Content-Type: application/json}class GetZabbix:def __init__(self):#用户信息self.username "****"self.password "****"self.url "http://192.168.0.*/zabbix/api_jsonrpc.php"self.token self.get…

C++17 常用新特性:带初始化的 if 和 switch 语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删早在2016-03-14&#xff0c;Thomas Kppe 在https://wg21.link/p0305r0中就提出了在if和switch使用初始化语句&#xff0c;使用时的代码示例如下&…

python回归预测例子_案例实战 | 逻辑回归实现客户流失预测(附Python代码与源数据)...

关注一下~&#xff0c;更多商业数据分析案例等你来撩前言利用逻辑回归进行客户流失预警建模中涵盖了许多比较细的知识点&#xff0c;思维导图只展示了极小的一部分&#xff0c;相关知识点链接将穿插在文中。(源数据源代码空降文末获取)数据读入数据属性说明数据探索简洁版本&am…

latex中算法命令_比较Java 8中的命令式和功能性算法

latex中算法命令Mario Fusco受欢迎的推文令人印象深刻&#xff0c;显示了类似算法的命令性和功能性方法之间的主要区别实际上是&#xff1a; 势在必行–功能分离pic.twitter.com/G2cC6iBkDJ — Mario Fusco&#xff08;mariofusco&#xff09; 2015年3月1日 两种算法都做同样…

四大语言加强统治地位,Rust威胁C/C++

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删TIOBE 公布了 2022 年 10 月的编程语言排行榜。截至今日&#xff0c;Python、Java、C 和 C 语言已经在 TIOBE 榜单的前 4 位盘踞了相当长的一段时…

sql计算留存_SQL无所不能:DBA宝妈宝爸系列分享

原文链接&#xff1a;https://www.modb.pro/db/22315 目前程序从功能上其实已经完全满足客户(当然我这里的客户都是指媳妇儿)需求&#xff0c;具体可参考&#xff1a;使用SQL计算宝宝每次吃奶的时间间隔 - Part1&#xff1a;分析函数的使用使用SQL计算宝宝每次吃奶的时间间隔 -…

java8流分组 性能_Java性能教程– Java 8流有多快?

java8流分组 性能在此JAX Magazine的预览预览中&#xff0c;JAX伦敦发言人Angelika Langer为使用Java流的任何人回答了最重要的问题&#xff1a;它们真的更快吗&#xff1f; Java 8是JDK收集框架的主要补充&#xff0c;即流​​API。 与集合相似&#xff0c;流表示元素序列。 …

对 int 变量赋值的操作是原子的吗?为什么?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删这个是在面试的时候遇到的问题&#xff0c;当时没有答出来。回到家以后查了查&#xff0c;整理记录下来。原问题&#xff1a;什么指令集支持原子…

C语言知识总结:if-else判断语句和switch-case选择语句

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删1、if-else判断语句一个基本的if语句由一个关键字if开头&#xff0c;跟上在括号&#xff08;&#xff09;里边的是表示逻辑条件的表达式&#xf…

hibernate性能_改善Hibernate应用程序性能的7种方法

hibernate性能Hibernate是将Java Web应用程序与SQL数据库集成的好工具&#xff0c;但是当应用程序的响应时间开始显着增加时&#xff0c;我们应该怎么做&#xff1f; 当我们怀疑应用程序是否会随着客户群的增长而扩展时&#xff0c;我们该怎么办&#xff1f; 在花大价钱托管或…

python 坐标轴 单位刻度_Python学习笔记(matplotlib篇)--坐标轴刻度

python学习笔记--坐标轴刻度所用的库及环境:ide:pycharmpython环境&#xff1a;python3.7matplotlib: matplotlib 1.11numpy&#xff1a; numpy1.15.datetime &#xff1a;datetime坐标轴刻度概念当需要把x&#xff0c;y坐标轴刻度调整的更密集些或者更宽松点学习如何调整x&…

为什么永远不会有语言取代C/C++?

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删数以百计的 C/C 替代品将会出现&#xff0c;但 C/C 将永远与我们同在&#xff01;每个 CPU 都带有一种称为 ISA&#xff08;指令集架构&#xff…