Apache Flink具有两个关系API-Table API和SQL-用于统一流和批处理。Table API是用于Scala和Java的语言集成查询API,它允许以非常直观的方式例如使用关系运算符(选择,过滤和联接等)来进行查询。Flink的SQL基于标准的Apache Calcite。无论输入是批处理输入(DataSet)还是流输入(DataStream),在两个接口中指定的查询都具有相同的语义并指定相同的结果。
根据目标编程语言,您需要将Java或Scala API添加到项目中,以便使用Table API和SQL定义管道:
org.apache.flink
flink-table-api-java-bridge_2.11
1.9.0
provided
org.apache.flink
flink-table-api-scala-bridge_2.11
1.9.0
provided
此外,如果要在IDE中本地运行Table API和SQL程序,则必须添加以下一组模块之一,具体取决于要使用的计划程序:
org.apache.flink
flink-table-planner_2.11
1.9.0
provided
org.apache.flink
flink-table-planner-blink_2.11
1.9.0
provided
在内部,表生态系统的一部分在Scala中实现。因此,请确保为批处理和流应用程序都添加以下依赖项:
org.apache.flink
flink-streaming-scala_2.11
1.9.0
provided
如果要实现与Kafka或一组用户定义的函数进行交互的自定义格式,则以下依赖关系就足够了,并且可以用于SQL Client的JAR文件:
org.apache.flink
flink-table-common
1.9.0
provided
当前,该模块包括以下扩展点:
SerializationSchemaFactory
DeserializationSchemaFactory
ScalarFunction
TableFunction
AggregateFunction
// create a TableEnvironment for specific planner batch or streaming
// **********************
// 使用原Flink的Planner进行流式处理
// **********************
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.table.api.EnvironmentSettings
import org.apache.flink.table.api.scala.StreamTableEnvironment
val fsSettings = EnvironmentSettings.newInstance().useOldPlanner().inStreamingMode().build()
val fsEnv = StreamExecutionEnvironment.getExecutionEnvironment
val fsTableEnv = StreamTableEnvironment.create(fsEnv, fsSettings)
// or val fsTableEnv = TableEnvironment.create(fsSettings)
// ******************
// 使用原Flink的Planner进行批式处理
// ******************
import org.apache.flink.api.scala.ExecutionEnvironment
import org.apache.flink.table.api.scala.BatchTableEnvironment
val fbEnv = ExecutionEnvironment.getExecutionEnvironment
val fbTableEnv = BatchTableEnvironment.create(fbEnv)
// **********************
// 使用BLINK的Planner进行流式处理
// **********************
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.table.api.EnvironmentSettings
import org.apache.flink.table.api.scala.StreamTableEnvironment
val bsEnv = StreamExecutionEnvironment.getExecutionEnvironment
val bsSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inStreamingMode().build()
val bsTableEnv = StreamTableEnvironment.create(bsEnv, bsSettings)
// or val bsTableEnv = TableEnvironment.create(bsSettings)
// ******************
// 使用BLINK的Plan批ner进行流式处理
// ******************
import org.apache.flink.table.api.{EnvironmentSettings, TableEnvironment}
val bbSettings = EnvironmentSettings.newInstance().useBlinkPlanner().inBatchMode().build()
val bbTableEnv = TableEnvironment.create(bbSettings)
//以上为创建一个TableEnvironment的几种不同的场景
// register a Table
tableEnv.registerTable("table1", ...) // or
tableEnv.registerTableSource("table2", ...) // or
tableEnv.registerExternalCatalog("extCat", ...)
// register an output Table
tableEnv.registerTableSink("outputTable", ...);
// create a Table from a Table API query
val tapiResult = tableEnv.scan("table1").select(...)
// create a Table from a SQL query
val sqlResult = tableEnv.sqlQuery("SELECT ... FROM table2 ...")
// emit a Table API result Table to a TableSink, same for SQL result
tapiResult.insertInto("outputTable")
// execute
tableEnv.execute("scala_job")
###Table API对列的操作
//执行字段添加操作。如果添加的字段已经存在,它将引发异常
val orders = tableEnv.scan("Orders");
val result = orders.addColumns(concat('c, "Sunny"))
//如果添加列名称与现有列名称相同,则现有字段将被替换
val result = orders.addOrReplaceColumns(concat('c, "Sunny") as 'desc)
//执行字段删除操作。字段表达式应该是字段引用表达式,并且只能删除现有字段。
val orders = tableEnv.scan("Orders")
val result = orders.dropColumns('b, 'c)
//执行字段重命名操作。字段表达式应该是别名表达式,并且只有现有字段可以重命名。
val orders = tableEnv.scan("Orders");
val result = orders.renameColumns('b as 'b2, 'c as 'c2)
import org.apache.flink.table.api.scala._
import org.apache.flink.api.scala._
//Table API使用Scala隐式。确保导入以上隐式进行隐式转换
// register Orders table
tableEnv.registerTable("Orders", Orders)
// ******************
//1.使用Table API进行表的查询
// ******************
val orders = tableEnv.scan("Orders")
// compute revenue for all customers from France
//以单个记号(')开头来引用表的属性
val revenue = orders
.filter('cCountry === "France") //筛选
.groupBy('cID, 'cName) //分组
.select('cID, 'cName, 'revenue.sum AS 'revSum) //查询计算
// ******************
//2.使用SQL进行表的查询
// ******************
val revenue = tableEnv.sqlQuery(
"""
|SELECT cID, cName, SUM(revenue) AS revSum
|FROM Orders
|WHERE cCountry = 'FRANCE'
|GROUP BY cID, cName
"""
.stripMargin) //stripMargin 删除由空格或控制字符组成的前导前缀
row 字段 整体可以理解为一个map key为字段的名称,value为字段的类型
使用场景之一:
使用flink Sql 去读取kafka中的json格式 ,json数据中再次嵌套数据时使用
{
"market_id":"108",
"info":{
"market_name":"北京商城",
"area":"北京"
}
}
针对上面json格式Flink Sql DDl中定义语句:
market_id int,
info Row
**注意:**Flink SQL中如果想要使用关键字作为字段名称,需要用反引号( ``)引起来
Flink SQL 中定义HBase维表时,具体使用的字段的数据类型要和 HBase 表具体存储的字段类型保持一致
Flink SQL 不支持short类型或者其它自定义类型
目前,Blink仅支持ROW_NUMBER
作为窗口功能。
val env = StreamExecutionEnvironment.getExecutionEnvironment
val tableEnv = TableEnvironment.getTableEnvironment(env)
// read a DataStream from an external source
val ds: DataStream[(String, String, String, Long)] = env.addSource(...)
// register the DataStream under the name "ShopSales"
tableEnv.registerDataStream("ShopSales", ds, 'product_id, 'category, 'product_name, 'sales)
// select top-5 products per category which have the maximum sales.
val result1 = tableEnv.sqlQuery(
"""
|SELECT product_id, category, product_name, sales -- 优化 省略查询row_num字段
|FROM (
| SELECT *,
| ROW_NUMBER() OVER (PARTITION BY category ORDER BY sales DESC) as row_num
| FROM ShopSales)
|WHERE row_num <= 5
"""
.stripMargin)
为了将上述查询输出到外部存储并获得正确的结果,外部存储必须具有与Top-N查询相同的唯一键。在上面的示例查询中,如果product_id_是查询的唯一键,则外部表也应具有product_id作为唯一键。
val orders: Table = tableEnv.scan("Orders") // schema (a, b, c, rowtime)
val result: Table = orders
.filter('a.isNotNull && 'b.isNotNull && 'c.isNotNull) //过滤空值
.select('a.lowerCase() as 'a, 'b, 'rowtime)
.window(Tumble over 1.hour on 'rowtime as 'hourlyWindow)//设定滚动窗口时间间隔1小时
.groupBy('hourlyWindow, 'a) //分组
.select('a, 'hourlyWindow.end as 'hour, 'b.avg as 'avgBillingAmount)
//每个小时计算并产生a平均帐单金额b