Flink教程(16)- Flink Table与SQL

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: Flink教程(16)- Flink Table与SQL

01 引言

在前面的博客,我们学习了Flink的一些高级API,有兴趣的同学可以参阅下:

在前面已经讲了Flink的批流一体API以及高级API了,接下来主要讲的是FlinkTableSQL

02 Table API & SQL 介绍

2.1 Flink Table模块

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/

Flink的Table模块包括 Table APISQL

  • Table API: 是一种类SQLAPI,通过Table API,用户可以像操作表一样操作数据,非常直观和方便;
  • SQL:作为一种声明式语言,有着标准的语法和规范,用户可以不用关心底层实现即可进行数据的处理,非常易于上手。

Flink Table APISQL 的实现上有80%左右的代码是公用的。作为一个流批统一的计算引擎,FlinkRuntime 层是统一的。

2.2 Table API & SQL特点

Flink之所以选择将 Table API & SQL作为未来的核心 API,是因为其具有一些非常重要的特点:

  • 声明式:用户只关心做什么,不用关心怎么做
  • 高性能:支持查询优化,可以获取更好的执行性能
  • 批流统一:相同的统计逻辑,既可以流模式运行,也可以批模式运行
  • 标准稳定:语义遵循SQL标准,不易变动
  • 易理解:语义明确,所见即所得

使用举例:

Table API SQL
tab.groupBy(“word”).select("word,count(1) as count") SELECT word,COUNT(*) AS cnt FROM MyTable GROUP BY word

2.3 Table API& SQL发展历程

自 2015 年开始,阿里巴巴开始调研开源流计算引擎,最终决定基于 Flink 打造新一代计算引擎,针对 Flink存在的不足进行优化和改进,并且在 2019 年初将最终代码开源,也就是Blink

Blink 在原来的 Flink 基础上最显著的一个贡献就是 Flink SQL的实现!

架构升级

查询处理器的选择 :Flink1.11之后Blink Query Processor查询处理器已经是默认的了。

03 开发准备

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/

3.1 添加依赖

<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>

解析:

  • flink-table-common:这个包中主要是包含 Flink PlannerBlink Planner一些共用的代码。
  • flink-table-api-java:这部分是用户编程使用的 API,包含了大部分的API
  • flink-table-api-scala:这里只是非常薄的一层,仅和 Table API的 Expression 和 DSL 相关。
  • 两个 Plannerflink-table-plannerflink-table-planner-blink
  • 两个 Bridgeflink-table-api-scala-bridgeflink-table-api-java-bridge

Flink PlannerBlink Planner 都会依赖于具体的 JavaAPI,也会依赖于具体的 Bridge,通过Bridge可以将 API 操作相应的转化为ScalaDataStreamDataSet,或者转化为JAVADataStream 或者Data Set

3.2 程序结构

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/common.html#structure-of-table-api-and-sql-programs

// create a TableEnvironment for specific planner batch or streaming
TableEnvironment tableEnv = ...; // see "Create a TableEnvironment" section
// create a Table
tableEnv.connect(...).createTemporaryTable("table1");
// register an output Table
tableEnv.connect(...).createTemporaryTable("outputTable");
// create a Table object from a Table API query
Table tapiResult = tableEnv.from("table1").select(...);
// create a Table object from a SQL query
Table sqlResult  = tableEnv.sqlQuery("SELECT ... FROM table1 ... ");
// emit a Table API result Table to a TableSink, same for SQL result
TableResult tableResult = tapiResult.executeInsert("outputTable");
tableResult...

3.3 API

3.3.1 获取环境

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/common.html#create-a-tableenvironment

// **********************
// 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 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);

3.3.3 创建表

// 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);
tableEnvironment
  .connect(...)
  .withFormat(...)
  .withSchema(...)
  .inAppendMode()
  .createTemporaryTable("MyTable")

3.3.4 查询表

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

SQL:

// 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.3.5 写出表

// 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");

3.3.6 与DataSet/DataStream集成

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/common.html#integration-with-datastream-and-dataset-api

3.3.6.1 从DataStream和DataSet创建视图

Create a View from a DataStream or DataSet

// get StreamTableEnvironment
// registration of a DataSet in a BatchTableEnvironment is equivalent
StreamTableEnvironment tableEnv = ...;
// see "Create a TableEnvironment" section
DataStream<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"));
3.3.6.2 转换DataStream和DataSet到表

Convert a DataStream or DataSet into a Table:

// get StreamTableEnvironment// registration of a DataSet in a BatchTableEnvironment is equivalent
StreamTableEnvironment tableEnv = ...;
// see "Create a TableEnvironment" section
DataStream<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.3.6.3 转换表到DataStream

Convert a Table into a DataStream

  • 追加模式(Append Mode):只有当动态表仅通过插入更改进行修改时,才能使用此模式,即,它是仅追加模式,并且以前发出的结果从不更新;
  • 撤回模式(Retract Mode):此模式始终可用。它使用布尔标志对插入和删除更改进行编码。
// 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);
3.3.6.3 转换表到DataSet

Convert a Table into a 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 TypeInformationTupleTypeInfo<Tuple2<String, Integer>> tupleType = new TupleTypeInfo<>(
  Types.STRING(),
  Types.INT());
DataSet<Tuple2<String, Integer>> dsTuple =
  tableEnv.toDataSet(table, tupleType);

3.3.7 TableAPI

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/tableApi.html

3.3.8 SQL

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/sql/

04 相关概念

参考:https://ci.apache.org/projects/flink/flink-docs-release-1.12/dev/table/streaming/dynamic_tables.html

4.1 动态表和连续查询

Flink中,它把针对无界流的表称之为Dynamic Table(动态表)。它是Flink Table APISQL的核心概念,顾名思义,它表示了Table是不断变化的。

我们可以这样来理解,当我们用FlinkAPI,建立一个表,其实把它理解为建立一个逻辑结构,这个逻辑结构需要映射到数据上去。

Flink source源源不断的流入数据,就好比每次都往表上新增一条数据。表中有了数据,我们就可以使用SQL去查询了。要注意一下,流处理中的数据是只有新增的,所以看起来数据会源源不断地添加到表中。

动态表也是一种表,既然是表,就应该能够被查询。我们来回想一下原先我们查询表的场景。

打开编译工具,编写一条SQL语句

  • SQL语句放入到mysql的终端执行
  • 查看结果
  • 再编写一条SQL语句
  • 再放入到终端执行
  • 再查看结果
  • ……如此反复

而针对动态表,Flinksource端肯定是源源不断地会有数据流入,然后我们基于这个数据流建立了一张表,再编写SQL语句查询数据,进行处理。这个SQL语句一定是不断地执行的,而不是只执行一次。

注意:针对流处理的SQL绝对不会像批式处理一样,执行一次拿到结果就完了。而是会不停地执行,不断地查询获取结果处理。所以,官方给这种查询方式取了一个名字,叫Continuous Query,中文翻译过来叫连续查询。而且每一次查询出来的数据也是不断变化的。

该示意图描述了:我们通过建立动态表和连续查询来实现在无界流中的SQL操作。

大家也可以看到,在Continuous上面有一个State,表示查询出来的结果会存储在State中,再下来Flink最终还是使用流来进行处理。

所以,我们可以理解为FlinkTable APISQL,是一个逻辑模型,通过该逻辑模型可以让我们的数据处理变得更加简单。

4.2 表与Stream的转换

4.2.1 表中的Update和Delete

我们前面提到的表示不断地 Append,表的数据是一直累加的,因为表示对接Source的,Source是不会有update的,但如果我们编写了一个SQL。这个SQL看起来是这样的:

SELECT user, sum(money) FROM order GROUP BY user;

当执行一条SQL语句之后,这条语句的结果还是一个表,因为在Flink中执行的SQLContinuous Query,这个表的数据是不断变化的。新创建的表存在Update的情况。仔细看下下面的示例,例如:

  • 第一条数据,张三,2000,执行这条SQL语句的结果是,张三,2000
  • 第二条数据,李四,1500,继续执行这条SQL语句,结果是,张三,2000 | 李四,1500
  • 第三条数据,张三,300,继续执行这条SQL语句,结果是,张三,2300 | 李四,1500
  • ….

大家发现了吗,现在数据结果是有Update的,张三一开始是2000,但后面变成了2300。

那还有删除的情况吗?有的,看下面这条SQL语句:

SELECT t1.`user`, SUM(t1.`money`) FROM t_order t1
WHERE
NOT EXISTS (SELECT T2.`user`AS TOTAL_MONEY FROM t_order t2 WHERE T2.`user` = T1.`user` GROUP BY t2.`user` HAVING SUM(T2.`money`) > 3000)
GROUP BY t1.`user`GROUP BY t1.`user`
  • 第一条数据,张三,2000,执行这条SQL语句的结果是,张三,2000
  • 第二条数据,李四,1500,继续执行这条SQL语句,结果是,张三,2000 | 李四,1500
  • 第三条数据,张三,300,继续执行这条SQL语句,结果是,张三,2300 | 李四,1500
  • 第四条数据,张三,800,继续执行这条SQL语句,结果是,李四,1500

因为张三的消费的金额已经超过了3000,所以SQL执行完后,张三是被处理掉了。从数据的角度来看,它不就是被删除了吗?

通过上面的两个示例,可以知道在Flink SQL中,对接Source的表都是Append-only的,不断地增加,执行一些SQL生成的表,这个表可能是要UPDATE的、也可能是要INSERT的。

4.2.2 对表的编码操作

我们前面说到过,表是一种逻辑结构,而Flink中的核心还是Stream,所以,Table最终还是会以Stream方式来继续处理,如果是以Stream方式处理,最终Stream中的数据有可能会写入到其他的外部系统中,例如:将Stream中的数据写入到MySQL中。

我们前面也看到了,表是有可能会UPDATEDELETE的,那么如果是输出到MySQL中,就要执行UPDATEDELETE语句了,而DataStream我们在学习Flink的时候就学习过了,DataStream是不能更新、删除事件的。

如果对表的操作是INSERT,这很好办,直接转换输出就好,因为DataStream数据也是不断递增的。但如果一个TABLE中的数据被UPDATE了、或者被DELETE了,如果用流来表达呢? 因为流不可变的特征,我们肯定要对这种能够进行UPDATE/DELETETABLE做特殊操作。

解决方案:我们可以针对每一种操作,INSERT/UPDATE/DELETE都用一个或多个经过编码的事件来表示。例如:

  • 针对UPDATE,我们用两个操作来表达,[DELETE]数据+ [INSERT]数据。也就是先把之前的数据删除,然后再插入一条新的数据。
  • 针对DELETE,我们也可以对流中的数据进行编码,[DELETE]数据。

总体来说,我们通过对流数据进行编码,也可以告诉DataStream的下游,[DELETE]表示发出MySQLDELETE操作,将数据删除。用[INSERT]表示插入新的数据。

4.2.3 将表转换为三种不同编码方式的流

Flink中的Table API或者SQL支持三种不同的编码方式,分别是:

  • Append-only流
  • Retract流
  • Upsert流
4.2.3.1 Append-only流

INSERT操作对应。这种编码类型的流针对的是只会不断新增的Dynamic Table,这种方式好处理,不需要进行特殊处理,源源不断地往流中发送事件即可。

4.2.3.2 Retract流

这种流就和Append-only不太一样,上面的只能处理INSERT,如果表会发生DELETE或者UPDATEAppend-only编码方式的流就不合适了。

Retract流有几种类型的事件类型:

  • ADD MESSAGE:这种消息对应的就是INSERT操作。
  • RETRACT MESSAGE:直译过来叫撤回消息,这种消息对应的就是DELETE操作。

我们可以看到通过ADD MESSAGERETRACT MESSAGE可以很好的向外部系统表达删除和插入操作,那如何进行UPDATE呢?其实RETRACT MESSAGE + ADD MESSAGE即可(先把之前的数据进行删除,然后插入一条新的)

4.2.3.3 Upsert流

前面我们看到的RETRACT编码方式的流,实现UPDATE是使用DELETE + INSERT模式的。

大家想一下:在MySQL中我们更新数据的时候,肯定不会先DELETE掉一条数据,然后再插入一条数据,肯定是直接发出UPDATE语句执行更新。

Upsert编码方式的流,是能够支持Update的,这种效率更高。它同样有两种类型的消息:

  • UPSERT MESSAGE:这种消息可以表示要对外部系统进行Update或者INSERT操作
  • DELETE MESSAGE:这种消息表示DELETE操作。

Upsert流是要求必须指定Primary Key的,因为Upsert操作是要有Key的,Upsert流针对UPDATE操作用一个UPSERT MESSAGE就可以描述,所以效率会更高。

05 文末

本文主要讲解了Flink TableSQL的一些概念,谢谢大家的阅读,本文完!

相关实践学习
基于Hologres轻松玩转一站式实时仓库
本场景介绍如何利用阿里云MaxCompute、实时计算Flink和交互式分析服务Hologres开发离线、实时数据融合分析的数据大屏应用。
Linux入门到精通
本套课程是从入门开始的Linux学习课程,适合初学者阅读。由浅入深案例丰富,通俗易懂。主要涉及基础的系统操作以及工作中常用的各种服务软件的应用、部署和优化。即使是零基础的学员,只要能够坚持把所有章节都学完,也一定会受益匪浅。
目录
相关文章
|
2月前
|
SQL 存储 API
Flink实践:通过Flink SQL进行SFTP文件的读写操作
虽然 Apache Flink 与 SFTP 之间的直接交互存在一定的限制,但通过一些创造性的方法和技术,我们仍然可以有效地实现对 SFTP 文件的读写操作。这既展现了 Flink 在处理复杂数据场景中的强大能力,也体现了软件工程中常见的问题解决思路——即通过现有工具和一定的间接方法来克服技术障碍。通过这种方式,Flink SQL 成为了处理各种数据源,包括 SFTP 文件,在内的强大工具。
166 15
|
14天前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
1月前
|
SQL 数据管理 数据库
SQL语句实例教程:掌握数据查询、更新与管理的关键技巧
SQL(Structured Query Language,结构化查询语言)是数据库管理和操作的核心工具
|
2月前
|
SQL 安全 Go
SQL注入不可怕,XSS也不难防!Python Web安全进阶教程,让你安心做开发!
在Web开发中,安全至关重要,尤其要警惕SQL注入和XSS攻击。SQL注入通过在数据库查询中插入恶意代码来窃取或篡改数据,而XSS攻击则通过注入恶意脚本来窃取用户敏感信息。本文将带你深入了解这两种威胁,并提供Python实战技巧,包括使用参数化查询和ORM框架防御SQL注入,以及利用模板引擎自动转义和内容安全策略(CSP)防范XSS攻击。通过掌握这些方法,你将能够更加自信地应对Web安全挑战,确保应用程序的安全性。
85 3
|
1月前
|
SQL 大数据 API
大数据-132 - Flink SQL 基本介绍 与 HelloWorld案例
大数据-132 - Flink SQL 基本介绍 与 HelloWorld案例
45 0
|
2月前
|
SQL 安全 数据处理
揭秘数据脱敏神器:Flink SQL的神秘力量,守护你的数据宝藏!
【9月更文挑战第7天】在大数据时代,数据管理和处理尤为重要,尤其在保障数据安全与隐私方面。本文探讨如何利用Flink SQL实现数据脱敏,为实时数据处理提供有效的隐私保护方案。数据脱敏涉及在处理、存储或传输前对敏感数据进行加密、遮蔽或替换,以遵守数据保护法规(如GDPR)。Flink SQL通过内置函数和表达式支持这一过程。
74 2
|
2月前
|
SQL 大数据 数据处理
奇迹降临!解锁 Flink SQL 简单高效的终极秘籍,开启数据处理的传奇之旅!
【9月更文挑战第7天】在大数据处理领域,Flink SQL 因其强大功能与简洁语法成为开发者首选。本文分享了编写高效 Flink SQL 的实用技巧:理解数据特征及业务需求;灵活运用窗口函数(如 TUMBLE 和 HOP);优化连接操作,优先采用等值连接;合理选择数据类型以减少计算资源消耗。结合实际案例(如实时电商数据分析),并通过定期性能测试与调优,助力开发者在大数据处理中更得心应手,挖掘更多价值信息。
46 1
|
6月前
|
SQL Apache 流计算
Flink table&SQL 的使用
Flink table&SQL 的使用
55 0
|
SQL IDE Java
【Flink】(十)Flink Table API 和 Flink SQL 入门
【Flink】(十)Flink Table API 和 Flink SQL 入门
325 0
|
SQL API 流计算
Flink-Table-&-SQL
简介 Apache Flink具有两个关系API - 表API和SQL - 用于统一流和批处理。Table API是Scala和Java的语言集成查询API,允许以非常直观的方式组合来自关系运算符的查询,Table API和SQL接口彼此紧密集成,以及Flink的DataStream和DataSet API。
1181 0

热门文章

最新文章