Flink(十三)Flink 的table api与sql的基本概念、通用api介绍及入门示例

news/2024/5/20 19:05:00

Flink 系列文章

Flink(一)1.12.7或1.13.5详细介绍及本地安装部署、验证
Flink(二)1.13.5二种部署方式(Standalone、Standalone HA )、四种提交任务方式(前两种及session和per-job)验证详细步骤
Flink(三)flink重要概念(api分层、角色、执行流程、执行图和编程模型)及dataset、datastream详细示例入门和提交任务至on yarn运行
Flink(四)介绍Flink的流批一体、transformations的18种算子详细介绍、Flink与Kafka的source、sink介绍
Flink(五)source、transformations、sink的详细示例(一)
Flink(五)source、transformations、sink的详细示例(二)-source和transformation示例
Flink(五)source、transformations、sink的详细示例(三)-sink示例
Flink(六)Flink四大基石之Window详解与详细示例(一)
Flink(六)Flink四大基石之Window详解与详细示例(二)
Flink(七)Flink四大基石之Time和WaterMaker详解与详细示例(watermaker基本使用、kafka作为数据源的watermaker使用示例以及超出最大允许延迟数据的接收实现)
Flink(八)Flink四大基石之State概念、使用场景、持久化、批处理的详解与keyed state和operator state、broadcast state使用和详细示例
Flink(九)Flink四大基石之Checkpoint容错机制详解及示例(checkpoint配置、重启策略、手动恢复checkpoint和savepoint)
Flink(十)source、transformations、sink的详细示例(二)-source和transformation示例【补充示例】
Flink(十一)Flink配置flink-conf.yaml详细说明(HA配置、checkpoint、web、安全、zookeeper、historyserver、workers、zoo.cfg)
Flink(十二)Flink source和sink 的 clickhouse 详细示例
Flink(十三)Flink 的table api与sql的基本概念、通用api介绍
Flink(十四)Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性
Flink(十五)Flink 的table api与sql之流式概念-配置时间属性和如何处理更新结果
Flink(十六)Flink 的table api与sql之连接外部系统: 读写外部系统的连接器和格式
Flink(十七)Flink 的table api与sql之Table API: Table API 支持的操作
Flink(十八)Flink 的table api与sql之SQL: SQL 支持的操作和语法
Flink(十九)Flink 的table api与sql之内置函数: Table API 和 SQL 中的内置函数
Flink(二十)Flink 的table api与sql之SQL Client: 不用编写代码就可以尝试 Flink SQL,可以直接提交 SQL 任务到集群上
Flink(二十一)Flink 的table api与sql之table api与sql使用示例


文章目录

  • Flink 系列文章
  • 一、Table API & SQL介绍
    • 1、Table API & SQL 介绍
    • 2、maven依赖
    • 3、table api与sql章节介绍
  • 二、概念与通用 API
    • 1、两种计划器(Planner)的主要区别
    • 2、Table API 和 SQL 程序的结构
    • 3、创建 TableEnvironment
    • 4、在 Catalog 中创建表
      • 1)、临时表(Temporary Table)和永久表(Permanent Table)
      • 2)、创建表
      • 3)、扩展表标识符
    • 5、查询表
      • 1)、Table API
      • 2)、SQL
      • 3)、混用 Table API 和 SQL
    • 6、输出表
    • 7、翻译与执行查询
      • 1)、Blink planner(flink-table-planner-blink)
      • 2)、Flink old planner(flink-table-planner)
    • 8、与 DataStream 和 DataSet API 结合
      • 1)、通过 DataSet 或 DataStream 创建视图
      • 2)、将 DataStream 或 DataSet 转换成表
      • 3)、将表转换成 DataStream 或 DataSet
        • 1、将表转换成 DataStream
        • 2、将表转换成 DataSet
      • 4)、数据类型到 Table Schema 的映射
        • 1、原子类型
        • 2、Tuple类型(Scala 和 Java)和 Case Class类型(仅 Scala)
        • 3、POJO 类型 (Java 和 Scala)
        • 4、Row类型
    • 9、查询优化
      • 1)、Blink planner(flink-table-planner-blink)
      • 2)、Flink planner(flink-table-planner)
    • 10、解释表
  • 三、示例1:将DataStream数据转Table然后使用sql进行查询
    • 1、maven依赖
    • 2、实现
    • 3、验证结果


本文介绍了Table Api和Sql的概念、使用示例,接下来将针对该部分继续进行概念介绍,最后会给出综合使用示例。
本文是该系列中介绍Table API与SQL的第一篇,接下来将通过9篇内容介绍该部分内容。
本文分为3个部分,即table api和sql的概要介绍、概念及api和简单的入门示例。

一、Table API & SQL介绍

中文官网链接:https://nightlies.apache.org/flink/flink-docs-release-1.12/zh/dev/table/

1、Table API & SQL 介绍

Apache Flink 有两种关系型 API 来做流批统一处理:Table API 和 SQL。
Table API 是用于 Scala 和 Java 语言的查询API,它可以用一种非常直观的方式来组合使用选取、过滤、join 等关系型算子。
Flink SQL 是基于 Apache Calcite 来实现的标准 SQL。无论输入是连续的(流式)还是有界的(批处理),在两个接口中指定的查询都具有相同的语义,并指定相同的结果。

Table API 和 SQL 两种 API 是紧密集成的,以及 DataStream API。可以在这些 API 之间,以及一些基于这些 API 的库之间轻松的切换。 比如,可以先用 CEP 从 DataStream 中做模式匹配,然后用 Table API 来分析匹配的结果;或者可以用 SQL 来扫描、过滤、聚合一个批式的表,然后再跑一个 Gelly 图算法 来处理已经预处理好的数据。

Flink的Table模块包括 Table API 和 SQL:
Table API 是一种类SQL的API,通过Table API,用户可以像操作表一样操作数据,非常直观和方便
SQL作为一种声明式语言,有着标准的语法和规范,用户可以不用关心底层实现即可进行数据的处理,非常易于上手
Flink Table API 和 SQL 的实现上有80%左右的代码是公用的。作为一个流批统一的计算引擎,Flink 的 Runtime 层是统一的。

2、maven依赖

<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-api-java-bridge_2.11</artifactId><version>1.12.7</version><scope>provided</scope>
</dependency>
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-planner-blink_2.11</artifactId><version>1.12.7</version><scope>provided</scope>
</dependency>
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-scala_2.11</artifactId><version>1.12.7</version><scope>provided</scope>
</dependency>
<!--扩展依赖-->
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-common</artifactId><version>1.12.7</version><scope>provided</scope>
</dependency>

3、table api与sql章节介绍

1、公共概念和 API: Table API 和 SQL 公共概念以及 API
2、数据类型: 内置数据类型以及它们的属性
3、流式概念: Table API 和 SQL 中流式相关的文档,比如配置时间属性和如何处理更新结果
4、连接外部系统: 读写外部系统的连接器和格式
5、Table API: Table API 支持的操作
6、SQL: SQL 支持的操作和语法
7、内置函数: Table API 和 SQL 中的内置函数
8、SQL Client: 不用编写代码就可以尝试 Flink SQL,可以直接提交 SQL 任务到集群上
9、table api与sql示例

二、概念与通用 API

1、两种计划器(Planner)的主要区别

两种planner是指flink1.9版本之前使用的flink-table-planner和1.11版本以后默认使用的flink-table-planner-blink。一般称为该两种planner为old planner和blink planner。

  1. Blink 将批处理作业视作流处理的一种特例。严格来说,Table 和 DataSet 之间不支持相互转换,并且批处理作业也不会转换成
    DataSet 程序而是转换成 DataStream 程序,流处理作业也一样。
  2. Blink 计划器不支持 BatchTableSource,而是使用有界的 StreamTableSource 来替代。
  3. 旧计划器和 Blink 计划器中 FilterableTableSource 的实现是不兼容的。旧计划器会将 PlannerExpression 下推至FilterableTableSource,而 Blink 计划器则是将 Expression 下推。
  4. 基于字符串的键值配置选项仅在 Blink 计划器中使用。
  5. PlannerConfig 在两种计划器中的实现(CalciteConfig)是不同的。
  6. Blink 计划器会将多sink(multiple-sinks)优化成一张有向无环图(DAG),TableEnvironment 和
    StreamTableEnvironment 都支持该特性。旧计划器总是将每个sink都优化成一个新的有向无环图,且所有图相互独立。
  7. 旧计划器目前不支持 catalog 统计数据,而 Blink 支持。

2、Table API 和 SQL 程序的结构

示例代码

// create a TableEnvironment for specific planner batch or streaming
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// create an input Table
tableEnv.executeSql("CREATE TEMPORARY TABLE table1 ... WITH ( 'connector' = ... )");
// register an output Table
tableEnv.executeSql("CREATE TEMPORARY TABLE outputTable ... WITH ( 'connector' = ... )");// create a Table object from a Table API query
Table table2 = tableEnv.from("table1").select(...);
// create a Table object from a SQL query
Table table3 = tableEnv.sqlQuery("SELECT ... FROM table1 ... ");// emit a Table API result Table to a TableSink, same for SQL result
TableResult tableResult = table2.executeInsert("outputTable");
tableResult...

注意: Table API 和 SQL 查询可以很容易地集成并嵌入到 DataStream 或 DataSet 程序中。关于转换参考本文后面的章节。

3、创建 TableEnvironment

TableEnvironment 是 Table API 和 SQL 的核心概念。它负责:

  • 在内部的 catalog 中注册 Table
  • 注册外部的 catalog
  • 加载可插拔模块
  • 执行 SQL 查询
  • 注册自定义函数(scalar、table 或 aggregation)
  • 将 DataStream 或 DataSet 转换成 Table
  • 持有对 ExecutionEnvironment 或 StreamExecutionEnvironment 的引用

Table 总是与特定的 TableEnvironment 绑定。不能在同一条查询中使用不同 TableEnvironment 中的表,例如,对它们进行 join 或 union 操作。

TableEnvironment 可以通过静态方法 BatchTableEnvironment.create() 或者 StreamTableEnvironment.create() 在 StreamExecutionEnvironment 或者 ExecutionEnvironment 中创建,TableConfig 是可选项。TableConfig可用于配置TableEnvironment或定制的查询优化和转换过程(参见 本文查询优化章节)。

请确保选择与你的编程语言匹配的特定的计划器BatchTableEnvironment/StreamTableEnvironment。

如果两种计划器的 jar 包都在 classpath 中(默认行为),你应该明确地设置要在当前程序中使用的计划器。

  • Flink query
// **********************
// FLINK STREAMING QUERY
// **********************
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;EnvironmentSettings fsSettings = EnvironmentSettings.newInstance().useOldPlanner().inStreamingMode().build();
StreamExecutionEnvironment fsEnv = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment fsTableEnv = StreamTableEnvironment.create(fsEnv, fsSettings);
// or TableEnvironment fsTableEnv = TableEnvironment.create(fsSettings);// ******************
// FLINK BATCH QUERY
// ******************
import org.apache.flink.api.java.ExecutionEnvironment;
import org.apache.flink.table.api.bridge.java.BatchTableEnvironment;ExecutionEnvironment fbEnv = ExecutionEnvironment.getExecutionEnvironment();
BatchTableEnvironment fbTableEnv = BatchTableEnvironment.create(fbEnv);
  • Blink query
// **********************
// BLINK STREAMING QUERY
// **********************
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;StreamExecutionEnvironment bsEnv = StreamExecutionEnvironment.getExecutionEnvironment();
EnvironmentSettings bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
StreamTableEnvironment bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings);
// or TableEnvironment bsTableEnv = TableEnvironment.create(bsSettings);// ******************
// BLINK BATCH QUERY
// ******************
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.TableEnvironment;EnvironmentSettings bbSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inBatchMode().build();
TableEnvironment bbTableEnv = TableEnvironment.create(bbSettings);

注意: 如果/lib目录中只有一种计划器的 jar 包,则可以使用useAnyPlanner创建 EnvironmentSettings。

4、在 Catalog 中创建表

TableEnvironment 维护着一个由标识符(identifier)创建的表 catalog 的映射。标识符由三个部分组成:catalog 名称、数据库名称以及对象名称。如果 catalog 或者数据库没有指明,就会使用当前默认值(参见表标识符扩展章节中的例子)。

Table 可以是虚拟的(视图 VIEWS)也可以是常规的(表 TABLES)。视图 VIEWS可以从已经存在的Table中创建,一般是 Table API 或者 SQL 的查询结果。 表TABLES描述的是外部数据,例如文件、数据库表或者消息队列。

1)、临时表(Temporary Table)和永久表(Permanent Table)

表可以是临时的,并与单个 Flink 会话(session)的生命周期相关,也可以是永久的,并且在多个 Flink 会话和群集(cluster)中可见。

永久表需要 catalog(例如 Hive Metastore)以维护表的元数据。一旦永久表被创建,它将对任何连接到 catalog 的 Flink 会话可见且持续存在,直至被明确删除。

另一方面,临时表通常保存于内存中并且仅在创建它们的 Flink 会话持续期间存在。这些表对于其它会话是不可见的。它们不与任何 catalog 或者数据库绑定但可以在一个命名空间(namespace)中创建。即使它们对应的数据库被删除,临时表也不会被删除。

可以使用与已存在的永久表相同的标识符去注册临时表。临时表会屏蔽永久表,并且只要临时表存在,永久表就无法访问。所有使用该标识符的查询都将作用于临时表。
这可能对实验(experimentation)有用。它允许先对一个临时表进行完全相同的查询,例如只有一个子集的数据,或者数据是不确定的。一旦验证了查询的正确性,就可以对实际的生产表进行查询。

2)、创建表

  • 虚拟表
    在 SQL 的术语中,Table API 的对象对应于视图(虚拟表)。它封装了一个逻辑查询计划。它可以通过以下方法在 catalog 中创建:
// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// table is the result of a simple projection query 
Table projTable = tableEnv.from("X").select(...);// register the Table projTable as table "projectedTable"
tableEnv.createTemporaryView("projectedTable", projTable);

注意: 从传统数据库系统的角度来看,Table 对象与 VIEW 视图非常像。也就是,定义了 Table 的查询是没有被优化的, 而且会被内嵌到另一个引用了这个注册了的 Table的查询中。如果多个查询都引用了同一个注册了的Table,那么它会被内嵌每个查询中并被执行多次, 也就是说注册了的Table的结果不会被共享(注:Blink 计划器的TableEnvironment会优化成只执行一次)。

  • Connector Tables
    另外一个方式去创建 TABLE 是通过 connector 声明。Connector 描述了存储表数据的外部系统。存储系统例如 Apache Kafka 或者常规的文件系统都可以通过这种方式来声明。
tableEnvironment.connect(...).withFormat(...).withSchema(...).inAppendMode().createTemporaryTable("MyTable")

3)、扩展表标识符

表总是通过三元标识符注册,包括 catalog 名、数据库名和表名。

用户可以指定一个 catalog 和数据库作为 “当前catalog” 和”当前数据库”。有了这些,那么刚刚提到的三元标识符的前两个部分就可以被省略了。如果前两部分的标识符没有指定, 那么会使用当前的 catalog 和当前数据库。用户也可以通过 Table API 或 SQL 切换当前的 catalog 和当前的数据库。

标识符遵循 SQL 标准,因此使用时需要用反引号(`)进行转义。

TableEnvironment tEnv = ...;
tEnv.useCatalog("custom_catalog");
tEnv.useDatabase("custom_database");Table table = ...;// register the view named 'exampleView' in the catalog named 'custom_catalog'
// in the database named 'custom_database' 
tableEnv.createTemporaryView("exampleView", table);// register the view named 'exampleView' in the catalog named 'custom_catalog'
// in the database named 'other_database' 
tableEnv.createTemporaryView("other_database.exampleView", table);// register the view named 'example.View' in the catalog named 'custom_catalog'
// in the database named 'custom_database' 
tableEnv.createTemporaryView("`example.View`", table);// register the view named 'exampleView' in the catalog named 'other_catalog'
// in the database named 'other_database' 
tableEnv.createTemporaryView("other_catalog.other_database.exampleView", table);

5、查询表

1)、Table API

Table API 是关于 Scala 和 Java 的集成语言式查询 API。与 SQL 相反,Table API 的查询不是由字符串指定,而是在宿主语言中逐步构建。

Table API 是基于 Table 类的,该类表示一个表(流或批处理),并提供使用关系操作的方法。这些方法返回一个新的 Table 对象,该对象表示对输入 Table 进行关系操作的结果。 一些关系操作由多个方法调用组成,例如 table.groupBy(…).select(),其中 groupBy(…) 指定 table 的分组,而 select(…) 在 table 分组上的投影。

该链接中列出了所有支持的算子操作:https://nightlies.apache.org/flink/flink-docs-release-1.12/zh/dev/table/tableApi.html
以下示例展示了一个简单的 Table API 聚合查询:

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// register Orders table// scan registered Orders table
Table orders = tableEnv.from("Orders");
// compute revenue for all customers from France
Table revenue = orders.filter($("cCountry").isEqual("FRANCE")).groupBy($("cID"), $("cName").select($("cID"), $("cName"), $("revenue").sum().as("revSum"));// emit or convert Table
// execute query

2)、SQL

Flink SQL 是基于实现了SQL标准的 Apache Calcite 的。SQL 查询由常规字符串指定。

该链接 描述了Flink对流处理和批处理表的SQL支持:https://nightlies.apache.org/flink/flink-docs-release-1.12/zh/dev/table/sql/

下面的示例演示了如何指定查询并将结果作为 Table 对象返回。

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// register Orders table// compute revenue for all customers from France
Table revenue = tableEnv.sqlQuery("SELECT cID, cName, SUM(revenue) AS revSum " +"FROM Orders " +"WHERE cCountry = 'FRANCE' " +"GROUP BY cID, cName");// emit or convert Table
// execute query

如下的示例展示了如何指定一个更新查询,将查询的结果插入到已注册的表中

// get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// register "Orders" table
// register "RevenueFrance" output table// compute revenue for all customers from France and emit to "RevenueFrance"
tableEnv.executeSql("INSERT INTO RevenueFrance " +"SELECT cID, cName, SUM(revenue) AS revSum " +"FROM Orders " +"WHERE cCountry = 'FRANCE' " +"GROUP BY cID, cName");

3)、混用 Table API 和 SQL

Table API 和 SQL 查询的混用非常简单因为它们都返回 Table 对象:

  • 可以在 SQL 查询返回的 Table 对象上定义 Table API 查询。
  • 在 TableEnvironment 中注册的结果表可以在 SQL 查询的 FROM 子句中引用,通过这种方法就可以在 Table API 查询的结果上定义 SQL 查询。

6、输出表

Table 通过写入 TableSink 输出。TableSink 是一个通用接口,用于支持多种文件格式(如 CSV、Apache Parquet、Apache Avro)、存储系统(如 JDBC、Apache HBase、Apache Cassandra、Elasticsearch)或消息队列系统(如 Apache Kafka、RabbitMQ)。

批处理 Table 只能写入 BatchTableSink,而流处理 Table 需要指定写入 AppendStreamTableSink,RetractStreamTableSink 或者 UpsertStreamTableSink。

该链接可以获取更多关于可用 Sink 的信息以及如何自定义 TableSink:https://nightlies.apache.org/flink/flink-docs-release-1.12/zh/dev/table/sourceSinks.html

方法 Table.executeInsert(String tableName) 将 Table 发送至已注册的 TableSink。该方法通过名称在 catalog 中查找 TableSink 并确认Table schema 和 TableSink schema 一致。

下面的示例演示如何输出 Table:

/ get a TableEnvironment
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// create an output Table
final Schema schema = new Schema().field("a", DataTypes.INT()).field("b", DataTypes.STRING()).field("c", DataTypes.BIGINT());tableEnv.connect(new FileSystem().path("/path/to/file")).withFormat(new Csv().fieldDelimiter('|').deriveSchema()).withSchema(schema).createTemporaryTable("CsvSinkTable");// compute a result Table using Table API operators and/or SQL queries
Table result = ...
// emit the result Table to the registered TableSink
result.executeInsert("CsvSinkTable");

7、翻译与执行查询

两种计划器翻译和执行查询的方式是不同的。

1)、Blink planner(flink-table-planner-blink)

不论输入数据源是流式的还是批式的,Table API 和 SQL 查询都会被转换成 DataStream 程序。查询在内部表示为逻辑查询计划,并被翻译成两个阶段:

  • 优化逻辑执行计划
  • 翻译成 DataStream 程序

Table API 或者 SQL 查询在下列情况下会被翻译:

  • 当 TableEnvironment.executeSql() 被调用时。该方法是用来执行一个 SQL 语句,一旦该方法被调用, SQL 语句立即被翻译。
  • 当 Table.executeInsert() 被调用时。该方法是用来将一个表的内容插入到目标表中,一旦该方法被调用, TABLE API 程序立即被翻译。
  • 当 Table.execute() 被调用时。该方法是用来将一个表的内容收集到本地,一旦该方法被调用, TABLE API 程序立即被翻译。
  • 当 StatementSet.execute() 被调用时。Table (通过 StatementSet.addInsert() 输出给某个 Sink)和 INSERT 语句 (通过调用 StatementSet.addInsertSql())会先被缓存到 StatementSet 中,StatementSet.execute() 方法被调用时,所有的 sink 会被优化成一张有向无环图。
  • 当 Table 被转换成 DataStream 时(本文下面的与 DataStream 和 DataSet API 结合)。转换完成后,它就成为一个普通的 DataStream 程序,并会在调用 StreamExecutionEnvironment.execute() 时被执行。

从 1.11 版本开始,sqlUpdate 方法 和 insertInto 方法被废弃,从这两个方法构建的 Table 程序必须通过 StreamTableEnvironment.execute() 方法执行,而不能通过 StreamExecutionEnvironment.execute() 方法来执行

2)、Flink old planner(flink-table-planner)

Table API 和 SQL 查询会被翻译成 DataStream 或者 DataSet 程序, 这取决于它们的输入数据源是流式的还是批式的。查询在内部表示为逻辑查询计划,并被翻译成两个阶段:

  • 优化逻辑执行计划
  • 翻译成 DataStream 或 DataSet 程序

Table API 或者 SQL 查询在下列情况下会被翻译:

  • 当 TableEnvironment.executeSql() 被调用时。该方法是用来执行一个 SQL 语句,一旦该方法被调用, SQL 语句立即被翻译。
  • 当 Table.executeInsert() 被调用时。该方法是用来将一个表的内容插入到目标表中,一旦该方法被调用, TABLE API 程序立即被翻译。
  • 当 Table.execute() 被调用时。该方法是用来将一个表的内容收集到本地,一旦该方法被调用, TABLE API 程序立即被翻译。
  • 当 StatementSet.execute() 被调用时。Table (通过 StatementSet.addInsert() 输出给某个 Sink)和 INSERT 语句 (通过调用 StatementSet.addInsertSql())会先被缓存到 StatementSet 中,StatementSet.execute() 方法被调用时,所有的 sink 会被优化成一张有向无环图。
  • 对于 Streaming 而言,当Table 被转换成 DataStream 时(参阅与 DataStream 和 DataSet API 结合)触发翻译。转换完成后,它就成为一个普通的 DataStream 程序,并会在调用 StreamExecutionEnvironment.execute() 时被执行。对于 Batch 而言,Table 被转换成 DataSet 时(参阅与 DataStream 和 DataSet API 结合)触发翻译。转换完成后,它就成为一个普通的 DataSet 程序,并会在调用 ExecutionEnvironment.execute() 时被执行。

从 1.11 版本开始,sqlUpdate 方法 和 insertInto 方法被废弃。对于 Streaming 而言,如果一个 Table 程序是从这两个方法构建出来的,必须通过 StreamTableEnvironment.execute() 方法执行,而不能通过 StreamExecutionEnvironment.execute() 方法执行;对于 Batch 而言,如果一个 Table 程序是从这两个方法构建出来的,必须通过 BatchTableEnvironment.execute() 方法执行,而不能通过 ExecutionEnvironment.execute() 方法执行

8、与 DataStream 和 DataSet API 结合

在流处理方面两种计划器都可以与 DataStream API 结合。只有旧计划器可以与 DataSet API 结合。在批处理方面,Blink 计划器不能同两种计划器中的任何一个结合。
注意: 下文讨论的 DataSet API 只与旧计划起有关。
Table API 和 SQL 可以被很容易地集成并嵌入到 DataStream 和 DataSet 程序中。例如,可以查询外部表(例如从 RDBMS),进行一些预处理,例如过滤,投影,聚合或与元数据 join,然后使用 DataStream 或 DataSet API(以及在这些 API 之上构建的任何库,例如 CEP 或 Gelly)。相反,也可以将 Table API 或 SQL 查询应用于 DataStream 或 DataSet 程序的结果。

这种交互可以通过 DataStream 或 DataSet 与 Table 的相互转化实现。本节介绍这些转化是如何实现的。

1)、通过 DataSet 或 DataStream 创建视图

在 TableEnvironment 中可以将 DataStream 或 DataSet 注册成视图。结果视图的 schema 取决于注册的 DataStream 或 DataSet 的数据类型。请参阅文档 数据类型到 table schema 的映射获取详细信息。

注意: 通过 DataStream 或 DataSet 创建的视图只能注册成临时视图。

// get StreamTableEnvironment
// registration of a DataSet in a BatchTableEnvironment is equivalent
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" sectionDataStream<Tuple2<Long, String>> stream = ...// register the DataStream as View "myTable" with fields "f0", "f1"
tableEnv.createTemporaryView("myTable", stream);// register the DataStream as View "myTable2" with fields "myLong", "myString"
tableEnv.createTemporaryView("myTable2", stream, $("myLong"), $("myString"));

2)、将 DataStream 或 DataSet 转换成表

与在 TableEnvironment 注册 DataStream 或 DataSet 不同,DataStream 和 DataSet 还可以直接转换成 Table。

// get StreamTableEnvironment
// registration of a DataSet in a BatchTableEnvironment is equivalent
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" sectionDataStream<Tuple2<Long, String>> stream = ...// Convert the DataStream into a Table with default fields "f0", "f1"
Table table1 = tableEnv.fromDataStream(stream);// Convert the DataStream into a Table with fields "myLong", "myString"
Table table2 = tableEnv.fromDataStream(stream, $("myLong"), $("myString"));

3)、将表转换成 DataStream 或 DataSet

Table 可以被转换成 DataStream 或 DataSet。通过这种方式,定制的 DataSet 或 DataStream 程序就可以在 Table API 或者 SQL 的查询结果上运行了。

将 Table 转换为 DataStream 或者 DataSet 时,你需要指定生成的 DataStream 或者 DataSet 的数据类型,即Table 的每行数据要转换成的数据类型。通常最方便的选择是转换成 Row 。以下列表概述了不同选项的功能:

  • Row: 字段按位置映射,字段数量任意,支持 null 值,无类型安全(type-safe)检查。
  • POJO: 字段按名称映射(POJO 必须按Table 中字段名称命名),字段数量任意,支持 null 值,无类型安全检查。
  • Case Class: 字段按位置映射,不支持 null 值,有类型安全检查。
  • Tuple: 字段按位置映射,字段数量少于 22(Scala)或者 25(Java),不支持 null 值,无类型安全检查。
  • Atomic Type: Table 必须有一个字段,不支持 null 值,有类型安全检查。

1、将表转换成 DataStream

流式查询(streaming query)的结果表会动态更新,即,当新纪录到达查询的输入流时,查询结果会改变。因此,像这样将动态查询结果转换成 DataStream 需要对表的更新方式进行编码。

将 Table 转换为 DataStream 有两种模式:

  • Append Mode: 仅当动态 Table 仅通过INSERT更改进行修改时,才可以使用此模式,即,它仅是追加操作,并且之前输出的结果永远不会更新。
  • Retract Mode: 任何情形都可以使用此模式。它使用 boolean 值对 INSERT 和 DELETE 操作的数据进行标记。
// get StreamTableEnvironment. 
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// Table with two fields (String name, Integer age)
Table table = ...// convert the Table into an append DataStream of Row by specifying the class
DataStream<Row> dsRow = tableEnv.toAppendStream(table, Row.class);// convert the Table into an append DataStream of Tuple2<String, Integer> 
//   via a TypeInformation
TupleTypeInfo<Tuple2<String, Integer>> tupleType = new TupleTypeInfo<>(Types.STRING(),Types.INT());
DataStream<Tuple2<String, Integer>> dsTuple = tableEnv.toAppendStream(table, tupleType);// convert the Table into a retract DataStream of Row.
//   A retract stream of type X is a DataStream<Tuple2<Boolean, X>>. 
//   The boolean field indicates the type of the change. 
//   True is INSERT, false is DELETE.
DataStream<Tuple2<Boolean, Row>> retractStream = tableEnv.toRetractStream(table, Row.class);

关于动态表dynamic tables请参考链接:https://nightlies.apache.org/flink/flink-docs-release-1.12/zh/dev/table/streaming/dynamic_tables.html

一旦 Table 被转化为 DataStream,必须使用 StreamExecutionEnvironment 的 execute 方法执行该 DataStream 作业。

2、将表转换成 DataSet

将 Table 转换成 DataSet 的过程如下:

// get BatchTableEnvironment
BatchTableEnvironment tableEnv = BatchTableEnvironment.create(env);// Table with two fields (String name, Integer age)
Table table = ...// convert the Table into a DataSet of Row by specifying a class
DataSet<Row> dsRow = tableEnv.toDataSet(table, Row.class);// convert the Table into a DataSet of Tuple2<String, Integer> via a TypeInformation
TupleTypeInfo<Tuple2<String, Integer>> tupleType = new TupleTypeInfo<>(Types.STRING(),Types.INT());
DataSet<Tuple2<String, Integer>> dsTuple = tableEnv.toDataSet(table, tupleType);

一旦 Table 被转化为 DataSet,必须使用 ExecutionEnvironment 的 execute 方法执行该 DataSet 作业。

4)、数据类型到 Table Schema 的映射

Flink 的 DataStream 和 DataSet APIs 支持多样的数据类型。例如 Tuple(Scala 内置以及Flink Java tuple)、POJO 类型、Scala case class 类型以及 Flink 的 Row 类型等允许嵌套且有多个可在表的表达式中访问的字段的复合数据类型。其他类型被视为原子类型。下面,我们讨论 Table API 如何将这些数据类型类型转换为内部 row 表示形式,并提供将 DataStream 转换成 Table 的样例。

数据类型到 table schema 的映射有两种方式:基于字段位置或基于字段名称。

  • 基于字段位置
    基于位置的映射可在保持字段顺序的同时为字段提供更有意义的名称。这种映射方式可用于具有特定的字段顺序的复合数据类型以及原子类型。如 tuple、row 以及 case class 这些复合数据类型都有这样的字段顺序。然而,POJO 类型的字段则必须通过名称映射(参见下一章)。可以将字段投影出来,但不能使用as重命名。

定义基于位置的映射时,输入数据类型中一定不能存在指定的名称,否则 API 会假定应该基于字段名称进行映射。如果未指定任何字段名称,则使用默认的字段名称和复合数据类型的字段顺序,或者使用 f0 表示原子类型。

// get a StreamTableEnvironment, works for BatchTableEnvironment equivalently
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section;DataStream<Tuple2<Long, Integer>> stream = ...// convert DataStream into Table with default field names "f0" and "f1"
Table table = tableEnv.fromDataStream(stream);// convert DataStream into Table with field "myLong" only
Table table = tableEnv.fromDataStream(stream, $("myLong"));// convert DataStream into Table with field names "myLong" and "myInt"
Table table = tableEnv.fromDataStream(stream, $("myLong"), $("myInt"));
  • 基于字段名称
    基于名称的映射适用于任何数据类型包括 POJO 类型。这是定义 table schema 映射最灵活的方式。映射中的所有字段均按名称引用,并且可以通过 as 重命名。字段可以被重新排序和映射。

若果没有指定任何字段名称,则使用默认的字段名称和复合数据类型的字段顺序,或者使用 f0 表示原子类型。

// get a StreamTableEnvironment, works for BatchTableEnvironment equivalently
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" sectionDataStream<Tuple2<Long, Integer>> stream = ...// convert DataStream into Table with default field names "f0" and "f1"
Table table = tableEnv.fromDataStream(stream);// convert DataStream into Table with field "f1" only
Table table = tableEnv.fromDataStream(stream, $("f1"));// convert DataStream into Table with swapped fields
Table table = tableEnv.fromDataStream(stream, $("f1"), $("f0"));// convert DataStream into Table with swapped fields and field names "myInt" and "myLong"
Table table = tableEnv.fromDataStream(stream, $("f1").as("myInt"), $("f0").as("myLong"));

1、原子类型

Flink 将基础数据类型(Integer、Double、String)或者通用数据类型(不可再拆分的数据类型)视为原子类型。原子类型的 DataStream 或者 DataSet 会被转换成只有一条属性的 Table。属性的数据类型可以由原子类型推断出,还可以重新命名属性。

// get a StreamTableEnvironment, works for BatchTableEnvironment equivalently
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" sectionDataStream<Long> stream = ...// convert DataStream into Table with default field name "f0"
Table table = tableEnv.fromDataStream(stream);// convert DataStream into Table with field name "myLong"
Table table = tableEnv.fromDataStream(stream, $("myLong"));

2、Tuple类型(Scala 和 Java)和 Case Class类型(仅 Scala)

Flink 支持 Scala 的内置 tuple 类型并给 Java 提供自己的 tuple 类型。两种 tuple 的 DataStream 和 DataSet 都能被转换成表。可以通过提供所有字段名称来重命名字段(基于位置映射)。如果没有指明任何字段名称,则会使用默认的字段名称。如果引用了原始字段名称(对于 Flink tuple 为f0、f1 … …,对于 Scala tuple 为_1、_2 … …),则 API 会假定映射是基于名称的而不是基于位置的。基于名称的映射可以通过 as 对字段和投影进行重新排序。

// get a StreamTableEnvironment, works for BatchTableEnvironment equivalently
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" sectionDataStream<Tuple2<Long, String>> stream = ...// convert DataStream into Table with default field names "f0", "f1"
Table table = tableEnv.fromDataStream(stream);// convert DataStream into Table with renamed field names "myLong", "myString" (position-based)
Table table = tableEnv.fromDataStream(stream, $("myLong"), $("myString"));// convert DataStream into Table with reordered fields "f1", "f0" (name-based)
Table table = tableEnv.fromDataStream(stream, $("f1"), $("f0"));// convert DataStream into Table with projected field "f1" (name-based)
Table table = tableEnv.fromDataStream(stream, $("f1"));// convert DataStream into Table with reordered and aliased fields "myString", "myLong" (name-based)
Table table = tableEnv.fromDataStream(stream, $("f1").as("myString"), $("f0").as("myLong"));

3、POJO 类型 (Java 和 Scala)

Flink 支持 POJO 类型作为复合类型。确定 POJO 类型的规则记录参看链接:https://nightlies.apache.org/flink/flink-docs-release-1.12/zh/dev/types_serialization.html#pojos

在不指定字段名称的情况下将 POJO 类型的 DataStream 或 DataSet 转换成 Table 时,将使用原始 POJO 类型字段的名称。名称映射需要原始名称,并且不能按位置进行。字段可以使用别名(带有 as 关键字)来重命名,重新排序和投影。

// get a StreamTableEnvironment, works for BatchTableEnvironment equivalently
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// Person is a POJO with fields "name" and "age"
DataStream<Person> stream = ...// convert DataStream into Table with default field names "age", "name" (fields are ordered by name!)
Table table = tableEnv.fromDataStream(stream);// convert DataStream into Table with renamed fields "myAge", "myName" (name-based)
Table table = tableEnv.fromDataStream(stream, $("age").as("myAge"), $("name").as("myName"));// convert DataStream into Table with projected field "name" (name-based)
Table table = tableEnv.fromDataStream(stream, $("name"));// convert DataStream into Table with projected and renamed field "myName" (name-based)
Table table = tableEnv.fromDataStream(stream, $("name").as("myName"));

4、Row类型

Row 类型支持任意数量的字段以及具有 null 值的字段。字段名称可以通过 RowTypeInfo 指定,也可以在将 Row 的 DataStream 或 DataSet 转换为 Table 时指定。Row 类型的字段映射支持基于名称和基于位置两种方式。字段可以通过提供所有字段的名称的方式重命名(基于位置映射)或者分别选择进行投影/排序/重命名(基于名称映射)。

// get a StreamTableEnvironment, works for BatchTableEnvironment equivalently
StreamTableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section// DataStream of Row with two fields "name" and "age" specified in `RowTypeInfo`
DataStream<Row> stream = ...// convert DataStream into Table with default field names "name", "age"
Table table = tableEnv.fromDataStream(stream);// convert DataStream into Table with renamed field names "myName", "myAge" (position-based)
Table table = tableEnv.fromDataStream(stream, $("myName"), $("myAge"));// convert DataStream into Table with renamed fields "myName", "myAge" (name-based)
Table table = tableEnv.fromDataStream(stream, $("name").as("myName"), $("age").as("myAge"));// convert DataStream into Table with projected field "name" (name-based)
Table table = tableEnv.fromDataStream(stream, $("name"));// convert DataStream into Table with projected and renamed field "myName" (name-based)
Table table = tableEnv.fromDataStream(stream, $("name").as("myName"));

9、查询优化

1)、Blink planner(flink-table-planner-blink)

Apache Flink 使用并扩展了 Apache Calcite 来执行复杂的查询优化。 这包括一系列基于规则和成本的优化,例如:

  • 基于 Apache Calcite 的子查询解相关
  • 投影剪裁
  • 分区剪裁
  • 过滤器下推
  • 子计划消除重复数据以避免重复计算
  • 特殊子查询重写,包括两部分:
    1、将 IN 和 EXISTS 转换为 left semi-joins
    2、将 NOT IN 和 NOT EXISTS 转换为 left anti-join
  • 可选 join 重新排序
    通过 table.optimizer.join-reorder-enabled 启用

注意: 当前仅在子查询重写的结合条件下支持 IN / EXISTS / NOT IN / NOT EXISTS。

优化器不仅基于计划,而且还基于可从数据源获得的丰富统计信息以及每个算子(例如 io,cpu,网络和内存)的细粒度成本来做出明智的决策。

高级用户可以通过 CalciteConfig 对象提供自定义优化,可以通过调用 TableEnvironment#getConfig#setPlannerConfig 将其提供给 TableEnvironment。

2)、Flink planner(flink-table-planner)

Apache Flink 利用 Apache Calcite 来优化和翻译查询。当前执行的优化包括投影和过滤器下推,子查询消除以及其他类型的查询重写。原版计划程序尚未优化 join 的顺序,而是按照查询中定义的顺序执行它们(FROM 子句中的表顺序和/或 WHERE 子句中的 join 谓词顺序)。

通过提供一个 CalciteConfig 对象,可以调整在不同阶段应用的优化规则集合。这个对象可以通过调用构造器 CalciteConfig.createBuilder() 创建,并通过调用 tableEnv.getConfig.setPlannerConfig(calciteConfig) 提供给 TableEnvironment。

10、解释表

Table API 提供了一种机制来解释计算 Table 的逻辑和优化查询计划。 这是通过 Table.explain() 方法或者 StatementSet.explain() 方法来完成的。Table.explain() 返回一个 Table 的计划。StatementSet.explain() 返回多 sink 计划的结果。它返回一个描述三种计划的字符串:

  • 关系查询的抽象语法树(the Abstract Syntax Tree),即未优化的逻辑查询计划,
  • 优化的逻辑查询计划,以及
  • 物理执行计划。

可以用 TableEnvironment.explainSql() 方法和 TableEnvironment.executeSql() 方法支持执行一个 EXPLAIN 语句获取逻辑和优化查询计划,请参阅 EXPLAIN 页面。

以下代码展示了一个示例以及对给定 Table 使用 Table.explain() 方法的相应输出:

StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);DataStream<Tuple2<Integer, String>> stream1 = env.fromElements(new Tuple2<>(1, "hello"));
DataStream<Tuple2<Integer, String>> stream2 = env.fromElements(new Tuple2<>(1, "hello"));// explain Table API
Table table1 = tEnv.fromDataStream(stream1, $("count"), $("word"));
Table table2 = tEnv.fromDataStream(stream2, $("count"), $("word"));
Table table = table1.where($("word").like("F%")).unionAll(table2);
System.out.println(table.explain());

上述例子的结果是:

== Abstract Syntax Tree ==
LogicalUnion(all=[true])LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')])FlinkLogicalDataStreamScan(id=[1], fields=[count, word])FlinkLogicalDataStreamScan(id=[2], fields=[count, word])== Optimized Logical Plan ==
DataStreamUnion(all=[true], union all=[count, word])DataStreamCalc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')])DataStreamScan(id=[1], fields=[count, word])DataStreamScan(id=[2], fields=[count, word])== Physical Execution Plan ==
Stage 1 : Data Sourcecontent : collect elements with CollectionInputFormatStage 2 : Data Sourcecontent : collect elements with CollectionInputFormatStage 3 : Operatorcontent : from: (count, word)ship_strategy : REBALANCEStage 4 : Operatorcontent : where: (LIKE(word, _UTF-16LE'F%')), select: (count, word)ship_strategy : FORWARDStage 5 : Operatorcontent : from: (count, word)ship_strategy : REBALANCE

以下代码展示了一个示例以及使用 StatementSet.explain() 的多 sink 计划的相应输出:

EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();
TableEnvironment tEnv = TableEnvironment.create(settings);final Schema schema = new Schema().field("count", DataTypes.INT()).field("word", DataTypes.STRING());tEnv.connect(new FileSystem().path("/source/path1")).withFormat(new Csv().deriveSchema()).withSchema(schema).createTemporaryTable("MySource1");
tEnv.connect(new FileSystem().path("/source/path2")).withFormat(new Csv().deriveSchema()).withSchema(schema).createTemporaryTable("MySource2");
tEnv.connect(new FileSystem().path("/sink/path1")).withFormat(new Csv().deriveSchema()).withSchema(schema).createTemporaryTable("MySink1");
tEnv.connect(new FileSystem().path("/sink/path2")).withFormat(new Csv().deriveSchema()).withSchema(schema).createTemporaryTable("MySink2");StatementSet stmtSet = tEnv.createStatementSet();Table table1 = tEnv.from("MySource1").where($("word").like("F%"));
stmtSet.addInsert("MySink1", table1);Table table2 = table1.unionAll(tEnv.from("MySource2"));
stmtSet.addInsert("MySink2", table2);String explanation = stmtSet.explain();
System.out.println(explanation);

多 sink 计划的结果是:

== Abstract Syntax Tree ==
LogicalLegacySink(name=[MySink1], fields=[count, word])
+- LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')])+- LogicalTableScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]])LogicalLegacySink(name=[MySink2], fields=[count, word])
+- LogicalUnion(all=[true]):- LogicalFilter(condition=[LIKE($1, _UTF-16LE'F%')]):  +- LogicalTableScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]])+- LogicalTableScan(table=[[default_catalog, default_database, MySource2, source: [CsvTableSource(read fields: count, word)]]])== Optimized Logical Plan ==
Calc(select=[count, word], where=[LIKE(word, _UTF-16LE'F%')], reuse_id=[1])
+- TableSourceScan(table=[[default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])LegacySink(name=[MySink1], fields=[count, word])
+- Reused(reference_id=[1])LegacySink(name=[MySink2], fields=[count, word])
+- Union(all=[true], union=[count, word]):- Reused(reference_id=[1])+- TableSourceScan(table=[[default_catalog, default_database, MySource2, source: [CsvTableSource(read fields: count, word)]]], fields=[count, word])== Physical Execution Plan ==
Stage 1 : Data Sourcecontent : collect elements with CollectionInputFormatStage 2 : Operatorcontent : CsvTableSource(read fields: count, word)ship_strategy : REBALANCEStage 3 : Operatorcontent : SourceConversion(table:Buffer(default_catalog, default_database, MySource1, source: [CsvTableSource(read fields: count, word)]), fields:(count, word))ship_strategy : FORWARDStage 4 : Operatorcontent : Calc(where: (word LIKE _UTF-16LE'F%'), select: (count, word))ship_strategy : FORWARDStage 5 : Operatorcontent : SinkConversionToRowship_strategy : FORWARDStage 6 : Operatorcontent : Mapship_strategy : FORWARDStage 8 : Data Sourcecontent : collect elements with CollectionInputFormatStage 9 : Operatorcontent : CsvTableSource(read fields: count, word)ship_strategy : REBALANCEStage 10 : Operatorcontent : SourceConversion(table:Buffer(default_catalog, default_database, MySource2, source: [CsvTableSource(read fields: count, word)]), fields:(count, word))ship_strategy : FORWARDStage 12 : Operatorcontent : SinkConversionToRowship_strategy : FORWARDStage 13 : Operatorcontent : Mapship_strategy : FORWARDStage 7 : Data Sinkcontent : Sink: CsvTableSink(count, word)ship_strategy : FORWARDStage 14 : Data Sinkcontent : Sink: CsvTableSink(count, word)ship_strategy : FORWARD

三、示例1:将DataStream数据转Table然后使用sql进行查询

1、maven依赖

<properties><encoding>UTF-8</encoding><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><java.version>1.8</java.version><scala.version>2.12</scala.version><flink.version>1.12.0</flink.version>
</properties><dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-api-scala-bridge_2.12</artifactId><version>${flink.version}</version><scope>provided</scope>
</dependency>
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-api-java-bridge_2.12</artifactId><version>${flink.version}</version><scope>provided</scope>
</dependency>
<!-- flink执行计划,这是1.9版本之前的-->
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-planner_2.12</artifactId><version>${flink.version}</version>
</dependency>
<!-- blink执行计划,1.11+默认的-->
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-planner-blink_2.12</artifactId><version>${flink.version}</version><scope>provided</scope>
</dependency>
<dependency><groupId>org.apache.flink</groupId><artifactId>flink-table-common</artifactId><version>${flink.version}</version><scope>provided</scope>
</dependency>

2、实现

  • java bean
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @author alanchan**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {private Long id;private String name;private double chinese;private double english;private double math;
}
  • 实现
import static org.apache.flink.table.api.Expressions.$;import java.util.Arrays;import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.EnvironmentSettings;
import org.apache.flink.table.api.Table;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;/*** @author alanchan**/
public class DataStream2Table {/*** @param args* @throws Exception*/public static void main(String[] args) throws Exception {// envStreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();EnvironmentSettings settings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build();StreamTableEnvironment tenv = StreamTableEnvironment.create(env, settings);// sourceDataStream<Student> studentDS = env.fromCollection(Arrays.asList(new Student(1L, "alan", 10, 20, 30), new Student(2L, "alanchan", 60, 70, 80),new Student(3L, "alanchanchn", 70, 80, 90), new Student(4L, "alanchn", 100, 100, 100)));// transformation// 将DataStream数据转Table,然后查询Table tableStudent = tenv.fromDataStream(studentDS, $("id"), $("name"), $("chinese"), $("english"), $("math"));String sql = "select *  from " + tableStudent + " where english > 20";Table resultTable = tenv.sqlQuery(sql);DataStream<Tuple2<Boolean, Student>> result = tenv.toRetractStream(resultTable, Student.class);// sinkresult.print();// executeenv.execute();}}

3、验证结果

15> (true,Student(id=2, name=alanchan, chinese=60.0, english=70.0, math=80.0))
1> (true,Student(id=4, name=alanchn, chinese=100.0, english=100.0, math=100.0))
16> (true,Student(id=3, name=alanchanchn, chinese=70.0, english=80.0, math=90.0))

以上,介绍了Table Api和Sql的概念、使用示例,接下来将针对该部分继续进行概念介绍,最后会给出综合使用示例。


http://www.mrgr.cn/p/77570626

相关文章

windows环境安装elasticsearch+kibana并完成JAVA客户端查询

下载elasticsearch和kibana安装包 原文连接&#xff1a;https://juejin.cn/post/7261262567304298554 elasticsearch官网下载比较慢&#xff0c;有时还打不开&#xff0c;可以通过https://elasticsearch.cn/download/下载&#xff0c;先找到对应的版本&#xff0c;最好使用迅…

Docker 入门终极指南[详细]

前言 富 Web 时代&#xff0c;应用变得越来越强大&#xff0c;与此同时也越来越复杂。集群部署、隔离环境、灰度发布以及动态扩容缺一不可&#xff0c;而容器化则成为中间的必要桥梁。 本节我们就来探索一下 Docker 的神秘世界&#xff0c;从零到一掌握 Docker 的基本原理与实…

【Java】零基础上手SpringBoot学习日记(day1)

前言 此帖为本人学习Springboot时的笔记&#xff0c;由于是个接触计算机一年左右的新手&#xff0c;也没有网站开发经验&#xff0c;所以有些地方的理解会比较浅显并且可能会出现错误&#xff0c;望大佬们多多包涵和指正。 Web应用开发 在我的理解中&#xff0c;Web应用的开发…

opencv-28 自适应阈值处理-cv2.adaptiveThreshold()

什么是自适应阈值处理? 对于色彩均衡的图像&#xff0c;直接使用一个阈值就能完成对图像的阈值化处理。但是&#xff0c;有时图像的色彩是不均衡的&#xff0c;此时如果只使用一个阈值&#xff0c;就无法得到清晰有效的阈值分割结果图像。 有一种改进的阈值处理技术&#xff…

基础概念:图片的卷积可视化结果

1. 前言 之前介绍过卷积的基本概念&#xff0c;具体的可以参考图片的卷积和池化操作&#xff0c;这里给出可视化的操作&#xff0c;因为卷积在初学的时候比较抽象&#xff0c;现在有时间就写写看&#xff0c;希望可以给初学的同学一点启发吧(这里前提是学过pytorch和相关的图像…

【C++入门到精通】C++入门 —— 类和对象(初始化列表、Static成员、友元、内部类、匿名对象)

目录 一、初始化列表 ⭕初始化列表概念 ⭕初始化列表的优点 ⭕使用场景 ⭕explicit关键字 二、Static成员 ⭕Static成员概念 &#x1f534;静态数据成员&#xff1a; &#x1f534;静态函数成员&#xff1a; ⭕使用静态成员的优点 ⭕使用静态成员的注意事项 三、友…

大数据面试题之Elasticsearch:每日三题(七)

大数据面试题之Elasticsearch:每日三题 1.Elasticsearch索引文档的流程&#xff1f;2.Elasticsearch更新和删除文档的流程&#xff1f;3.Elasticsearch搜索的流程&#xff1f; 1.Elasticsearch索引文档的流程&#xff1f; 协调节点默认使用文档ID参与计算(也支持通过routing)&a…

微服务项目,maven无法加载其他服务依赖

微服务项目&#xff0c;导入了工具类工程&#xff0c;但是一直报错&#xff0c;没有该类&#xff0c; 检查maven 这里的Maven的版本与idea版本不匹配可能是导致依赖加载失败的最重要原因 检查maven配置&#xff0c;我这是原来的maven&#xff0c;home 修改之后,就不报错了

秒级体验本地调试远程 k8s 中的服务

点击上方蓝色字体&#xff0c;选择“设为星标” 回复”云原生“获取基础架构实践 背景 在这个以k8s为云os的时代&#xff0c;程序员在日常的开发过程中&#xff0c;肯定会遇到各种问题&#xff0c;比如&#xff1a;本地开发完&#xff0c;需要部署到远程k8s集群&#xff0c;本地…

TCP/IP协议详解(二)

目录内容 TCP协议的可靠性 TCP的三次握手 TCP的四次挥手 C#中&#xff0c;TCP/IP建立 三次握手和四次挥手常见面试题 在上一篇文章中讲解了TCP/IP的由来以及报文格式&#xff0c;详情请见上一篇文章&#xff0c;现在接着来讲讲TCP/IP的可靠性以及通过代码的实现。 在TCP首部的…

算法通过村第二关-链表白银笔记|指定区间反转

文章目录 前言链表反转|指定区间内头插法&#xff1a;穿针引线法&#xff1a; 总结 前言 提示&#xff1a;人啊&#xff0c;果然跟花一样&#xff0c;开花前的等待无比漫长&#xff0c;绽放的魅力却转瞬即逝。 链表反转|指定区间内 参考题目&#xff1a;92. 反转链表 II - 力…

Vue(待续)

概念 一套用于构建用户界面的渐进式JavaScript框架 Vue可以自底向上逐层的应用&#xff1a; 简单应用:只需一个轻量小巧的核心库。 复杂应用:可以引入各式各样的Vue插件。 1.采用组件化模式&#xff0c;提高代码复用率、且让代码更好维护。 2.声明式编码&#xff0c;让编码人员…

【iOS】—— 持久化

文章目录 数据持久化的目的iOS中数据持久化方案数据持久化方式分类内存缓存磁盘缓存 沙盒机制获取应用程序的沙盒路径沙盒目录的获取方式 持久化数据存储方式XML属性列表Preferences偏好设置&#xff08;UserDefaults&#xff09;数据库存储什么是序列化和反序列化&#xff0c;…

DAY1,Qt [ 手动实现登录框(信息调试类,按钮类,行编辑器类,标签类的使用)]

1.手动实现登录框&#xff1b; ---mychat.h---头文件 #ifndef MYCHAT_H #define MYCHAT_H#include <QWidget> #include <QDebug> //打印信息 #include <QIcon> //图标 #include <QPushButton> //按钮 #include <QLineEdit> //行编辑器类 #in…

TCP/IP协议

TCP/IP 是一类协议系统&#xff0c;它是用于网络通信的一套协议集合 物理层 所谓的物理层&#xff0c;是指光纤、电缆或者电磁波等真实存在的物理媒介。这些媒介可以传送物理信号&#xff0c;比如亮度、电压或者振幅。对于数字应用来说&#xff0c;我们只需要两种物理信号来分别…

分冶算法 剑指 07 重建二叉树 排序算法:剑指45 把数组排成最小的数 10-I 斐波那契数列

来记录几个注意事项 1.vector容器里利用find&#xff08;&#xff09;函数 不同于map&#xff08;map有find方法&#xff09;&#xff0c;vector本身是没有find这一方法&#xff0c;其find是依靠algorithm来实现的。 所以要包含头文件 #include <iostream> #include <…

特定Adreno GPU的Android设备发生冻屏问题

1&#xff09;特定Adreno GPU的Android设备发生冻屏问题 ​2&#xff09;Unity版本升级后&#xff0c;iOS加载UnityFramework bundle闪退 3&#xff09;关于RectTransfrom.rect在屏幕空间中表示的相关问题 4&#xff09;Unity Mesh泄露问题 这是第345篇UWA技术知识分享的推送&a…

计算机网络——应用层

文章目录 **1 网络应用模型****2 域名系统DNS****3 文件传输协议FTP****4 电子邮件****4.1 电子邮件系统的组成结构****4.2 电子邮件格式与MIME****4.3 SMTP和POP3** **5 万维网WWW****5.1 HTTP** 1 网络应用模型 客户/服务器模型 C/S 服务器服务于许多来自其他称为客户机的主…

ElementUI Select选择器如何根据value值显示对应的label

修改前效果如图所示&#xff0c;数据值状态应显示为可用&#xff0c;但实际上仅显示了状态码1&#xff0c;并没有显示其对应的状态信息。在排查了数据类型对应关系问题后&#xff0c;并没有产生实质性影响&#xff0c;只好对代码进行了如下修改。 修改前代码&#xff1a; <…

CSS鼠标样式(cursor)

CSS cursor 属性值 属性值示意图描述auto默认值&#xff0c;由浏览器根据当前上下文确定要显示的光标样式default 默认光标&#xff0c;不考虑上下文&#xff0c;通常是一个箭头none不显示光标initial将此属性设置为其默认值inherit从父元素基础 cursor 属性的值context-menu…