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;等…

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

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

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" &…

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

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

四大语言加强统治地位,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计算宝宝每次吃奶的时间间隔 -…

对 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; 在花大价钱托管或…

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

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

sql 非主键每年自增_或许你不知道的 10 条 SQL 技巧

上文我们简述了 SQL 的一些进阶技巧&#xff0c;一些朋友觉得不过瘾&#xff0c;我们继续来下篇&#xff0c;再送你 10 个技巧一、 使用延迟查询优化 limit [offset], [rows]经常出现类似以下的 SQL 语句:SELECT * FROM film LIMIT 100000, 10offset 特别大!这是我司出现很多慢…

C语言最全入门笔记

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删c语言入门C语言一经出现就以其功能丰富、表达能力强、灵活方便、应用面广等特点迅速在全世界普及和推广。C语言不但执行效率高而且可移植性好&am…

junit测试spring_使用Spring JUnit规则进行参数化集成测试

junit测试springSpring 4.2附带了全新的JUnit规则&#xff1a; SpringClassRule和SpringMethodRule 。 使用JUnit规则的主要优点是让开发人员摆脱SpringJUnit4ClassRunner并在Spring集成测试中利用不同的JUnit运行器。 我认为Spring JUnit Rules的最大机会是易于创建参数化的集…

无处不在_Java无处不在:使用DukeScript在任何地方运行一次编写

无处不在在相当长一段时间内&#xff0c;Java都未能兑现“一次编写&#xff0c;随处运行”的承诺。 DukeScript希望通过在跨平台应用程序中实现视图和逻辑的清晰分离来改变这种状况。 在本文中&#xff0c;一个简单的场景用于介绍DukeScript的基础。 多年以来&#xff0c;Java…

C语言为何不会过时

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删评价任何一门编程语言&#xff0c;都是招人骂的。永远是这样。就像是春寒料峭的季节&#xff0c; 街上穿棉袄和穿单衣的擦肩而过&#xff0c;双方…

教你如何用一句话激怒IT人

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删在IT行业摸爬滚打这么久&#xff0c;你是否听过下面这些话&#xff1a;“这个很简单&#xff0c;我也是做程序出身的。”“预算砍掉一半&#xf…

java的网络编程有用吗_十大有用但又偏执的Java编程技术

java的网络编程有用吗经过一段时间的编码&#xff08;以我为例&#xff0c;大约20年左右&#xff0c;当您玩得开心时光飞逝&#xff09;&#xff0c;人们开始接受这些习惯。 因为&#xff0c;你知道... 任何可能出错的事情都会发生。 这就是为什么人们会采用“防御性编程”的原…

未来五年有颠覆性的IT技术都在这里

点击蓝字关注我们因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享来源于网络&#xff0c;侵删据福布斯杂志报道&#xff0c;在美国奥兰多举行的Gartner研讨会上&#xff0c;市场研究机构Gartner Research的副总裁兼资深研究员大卫卡利&…

java ee maven_真正释放Maven和Java EE的强大功能

java ee maven如果野心和愿景太复杂而无法使用&#xff0c;则它们可能会淘汰伟大的解决方案。 尽管Maven和Java EE是在整个Java行业中都已建立的良好技术&#xff0c;但是使用它们并使用其作者打算使用的所有技术和模式来设计项目可能非常棘手。 在开发过程中途&#xff0c;由…