Flink SQL
简单订单统计
假设有以下两个订单流数据,数据字段分别为用户ID、购买的商品名称、商品数量。
数据流A:
1L,"尺子",3
1L,"铅笔",4
3L,"橡皮",2
数据流B:
2L,"手表",3
2L,"笔记本",3
4L,"计算器",1
目标:合并两个流的数据,并筛选出商品数量大于2的订单数据。
import org.apache.flink.streaming.api.scala.StreamExecutionEnvironment
import org.apache.flink.table.api.{$,EnvironmentSettings}
import org.apache.flink.table.api.bridge.scala.StreamTableEnvironment
import org.apache.flink.streaming.api.scala._
import org.apache.flink.table.api_
/**
* Flink SQL统计订单流数据
* 知识点:DataStream转为Table、视图,Table转为DataStream
*/
object FlinkSQLDemo{
def main(args:Array[String]):Unit={
//创建流执行环境
val env=StreamExecutionEnvironment.getExecutionEnvironment
//创建EnvironmentSettings实例并设置参数
val settings=EnvironmentSettings
.newInstance() //创建一个用于创建EnvironmentSettings实例的构建器
.useBlinkPlanner() //将Blink计划器设置为所需的模块(默认)
.inStreamingMode() //设置组件以流模式工作,默认启用
.build() //创建一个不可变的EnvironmentSettings实例
//构建流式表执行环境StreamTableEnvironment
val tableEnv: StreamTableEnvironment=StreamTableEnvironment.create(env,settings)
//构建订单数据流A
val orderStreamA:DataStream[Order]=env.fromCollection(
List(Order(1L,"尺子",3),
Order(1L,"铅笔",4),
Order(3L,"橡皮",2)
)
)
//构建订单数据流B
val orderStreamA:DataStream[Order]=env.fromCollection(
List(Order(2L,"手表",3),
Order(2L,"笔记本",3),
Order(4L,"计算器",1)
)
)
//将DataStream转为Table,并指定Table的所有字段
val tableA: Table=tableEnv.fromDataStream(orderStreamA,$"user",$"product",$"amount")
//将Table的schema以摘要格式打印到控制台
tableA.printSchema()
//(
// 'user' BIGINT,
// 'product' STRING,
// 'user' INT,
//)
//将DataStream转为视图,视图名称为tableB,并指定视图的所有字段
tableEnv.createTemporaryView("tableB",orderStreamB,$("user"),$("product"),$("amount"))
//执行SQL查询,合并查询结果
println("tableA默认表名:"+tableA.toString)
val resultTable:Table=tableEnv.sqlQuery(
"SELECT * FROM " + tableA + " WHERE amount>2" +
"UNION ALL "+
"SELECT * FROM tableB WHERE amount > 2"
)
//将结果Table转为仅追加流
val dataStreamResult=tableEnv.toAppendStream[Order](resultTable)
//将流打印到控制台
dataStreamResult.print()
//触发程序执行
env.execute()
}
}
//创建订单样例
case class Order(user:Long,product:String,amount:Int)
在IDEA本地执行上述代码,控制台输出结果如下:
1> Order(1,铅笔,4)
11> Order(2,笔记本,3)
10> Order(2,手表,3)
12> Order(1,尺子,3)
计算产品类别销售额TopN
假设有一个存储产品类别销售额数据的文件sales.csv。
字段分别为日期、产品类别、销售额。
使用Flink SQL统计每一个产品类别销售额的前三名(分组求TopN)
在Maven项目中的pom.xml中添加支持读取CSV文件的Flink依赖:http://t.csdnimg.cn/bWIBB
Table API
Table API适用于Scala, Java和Python。
- Scala Table API利用了Scala表达式
- Java Table API既支持表达式DSL,也支持解析和转换为等效表达式的字符串
- Python Table API目前只支持解析和转换为等效表达式的字符串
Table API聚合查询代码示例:
//获得表环境TableEnvironment
val tableEnv: TableEnvironment=...
//注册一个Order表
//扫描(读取)已注册的Orders表,返回结果Table(相当于将已注册的表转为Table,便于后续操作)
val orders: Table=tableEnv.from("Orders")
//计算来自法国的所有客户的收入
val revenue:Table=Orders
.filter($"cCountry"==="FRANCE")
.groupby($"cID",$"cName")
.select($"cID",$"cName",$"revenue".sum AS "revSum")
//Table转DataStream
//执行查询
其中,读表语句val orders: Table=tableEnv.from("Orders")需注意:
从默认的Catalog和数据库中读取名称为tableName的表(或视图) | |
从指定的Catalog中读取名称为tableName的表 | |
使用转义从指定的Catalog中读取表(例如,数据库名称中的点必须转义) | |
订单分组计数
假设有一个名为Orders的已注册表具有属性a,b,c,rowtime。
下面分别使用Scala和Java Table API在批环境中对Order表进行处理。
扫描Orders表,按照字段a分组,并计算每组的数据行数。
Java Table API
// Java Table API需要导出的包
import org.apache.flink.table.api.*;
// 对于表达式DSL,需要导入静态包
import static org.apache.flink.table.api.Expressions.*;
// 环境配置
EnvironmentSettings settings=EnvironmentSettings
.newInstance()
.inStreamingMode()
.build();
TableEnvironment tEnv=TableEnvironment.create(settings);
// 向表环境注册一个Orders表
// ...
// 读取已注册的Orders表,返回结果Table(相当于将已注册的表转化为Table,便于后续操作)
Table orders=tEnv.from("Orders"); //字段:(a,b,c,rowtime)
//查询表
Table counts=orders
.groupBy($("a")) //按照a分组
.select($("a"),$("b").count().as("cnt"));
//将结果Table转为DataSet
DataSet<Row> result=tEnv.toDataSet(counts,Row.class);
result.print();
Scala Table API
import org.apache.flink.api.scala._
import org.apache.flink.table.scala._
import org.apache.flink.table.api.bridge.scala._
//环境配置
val settings=EnvironmentSettings
.newInstance()
.inStreamingMode()
.build();
val tEnv=TableEnvironment.create(settings);
//注册Orders表
//...
//读取已注册的Orders表,返回结果Table(相当于将已经注册的表转为Table,便于后续操作)
val orders=tEnv.from("Orders") //字段:(a,b,c,rowtime)
//查询并打印结果
val result=orders
.groupBy($"a") //按照a分组
.select($"a",$"b".count as "cnt")
.toDataSet[Row] //转为DataSet
.print()
每小时订单分组求平均值
扫描Orders表,过滤空值,并按照字段a分组,每小时计算一次各组数据中字段b的平均值。
Java Table API
//读取表Orders
Table orders=tEnv.from("Orders"); //字段:(a,b,c,rowtime)
//执行查询
Table result=orders
//过滤空值
.filter(
and(
$("a").isNotNull(),
$("b").isNotNull(),
$("c").isNotNull()
))
//字段a转为小写
.select($("a").lowerCase().as("a"),$("b"),$("rowtime"))
//1小时滚动窗口
.window(Tumble.over(lit(1).hours()).on($("rowtime")).as("hourlyWindow"))
//按照窗口和字段a分组
.groupBy($("hourlyWindow"),$("a"))
//查询字段a,窗口结束时间,字段b平均值
.select($("a"),$("hourlyWindow").end().as("hour"),$("b").avg().as("avgBillingAmount"));
Scala Table API
//读取Orders表
val orders:Table=tEnv.from("Orders") //字段:(a,b,c,rowtime)
//执行查询
val result:Table=orders
//过滤空值
.filter($"a".isNotNull && $"b".isNotNull && $"c".isNotNull)
//字段a转为小写
.select($"a".lowerCase() as "a",$"b",$"rowtime")
//1小时滚动窗口
.window(Tumble over 1.hour on $"rowtime" as "hourlyWindow")
//按照窗口,字段a分组
.groupBy($"hourlyWindow",$"a")
//查询字段a,窗口结束时间,字段b的平均值
.select($"a",$"hourlyWindow".end as "hour",$"b".avg as "avgBillingAmount")