窗口
窗口
Group Windows
Group Windows
是使用 window(w:GroupWindow)
子句定义的,并且必须由as子句指定一个别名。
为了按窗口对表进行分组,窗口的别名必须在 group by
子句中,像常规的分组字段一样引用
Table table = input
.window([w: GroupWindow] as "w") // 定义窗口,别名为 w
.groupBy("w, a") // 按照字段 a和窗口 w分组
.select("a, b.sum"); // 聚合
在select
语句中,我们除了可以获取到数据元素以外,还可以获取到窗口的元数据信息。
Table table = input
.window([w:Window] as "window") //
.groupBy("window" , "id") // 根据窗口聚合,窗口数据分配到每单个Task算子
.select("id" , "var1.sum","window.start","window.end","window.rowtime") // 指定val字段求和
Tumbling Windows
前面提到滚动窗口的窗口长度是固定的,窗口之间的数据不会重合。滚动窗口可以基于Evenet Time
、Process Time
以及Row-Count
来定义。如下实例:Table API
中滚动窗口使用Tumble Class
来创建,且分别基于Evenet Time
、Process Time
以及Row-Count
来定义窗口。
// 通过scan方法在CataLog中查询Sensors表
tableEnv.scan("Sensors")
// 十分钟事件语义下的滚动窗口
.window(Tumble.over("10.minutes").on("rowtime").as("w"));
// 十分钟过程语义下的滚动窗口
.window(Tumble.over("10.minutes").on("proctime").as("w"));
// 十行过程语义下的滚动窗口
.window(Tumble.over("10.rows").on("proctime").as("w"));
-
over
: 指定窗口的长度 -
on
: 定义了窗口基于的时间概念类型为EventTime
还是ProcessTime
,EventTime
对应着rowtime
,
ProcessTime
对应着proctime
-
as
: 将创建的窗口重命名,同时窗口名称需要在后续的孙子中使用
实际案例
val resultTable = sensorTable
.window(Tumble over 10.seconds on 'ts as 'tw) //每十秒统计一次,滚动窗口
.groupBy('id, 'tw)
.select('id, 'id.count, 'temperature.avg, 'tw.end) //end是当前窗口的结束时间
Sliding Windows
滑动窗口的长度也是固定的,但窗口与窗口之间的数据能够重合。滑动窗口可以基于Evenet Time
、Process Time
以及Row-Count
来定义。如下实例:Table API
中滑动窗口使用Slide Class
来创建,且分别基于Evenet Time
、Process Time
以及Row-Count
来定义窗口。
// 通过scan方法在CataLog中查询Sensors表
tableEnv.scan("Sensors")
.window(Slide.over("10.minutes").every("5.minutes").on("rowtime").as("w"));
.window(Slide.over("10.minutes").every("5.minutes").on("proctime").as("w"));
.window(Slide.over("10.rows").every("5.rows").on("proctime").as("w"));
-
over
: 定义窗口的长度,可以是时间或行计数间隔。 -
every
: 定义滑动间隔,可以是时间间隔也可以是行数。滑动间隔必须与大小间隔的类型相同。 -
on
: 定义了窗口基于的时间概念类型为EventTime
还是ProcessTime
,EventTime
对应着rowtime
,ProcessTime
对应着proctime
-
as
: 将创建的窗口重命名,同时窗口名称需要在后续的孙子中使用。
Session Windows
与Tumbling、Sliding
窗口不同的是,Session
窗口不需要指定固定的窗口时间,而是通过判断固定时间内数据的活跃性来切分窗口。例如 10 min内数据不接入则切分窗口并触发计算。Session
窗口只能基于EventTime
和ProcessTime
时间概念来定义,通过withGrap
操作符指定数据不活跃的时间Grap
,表示超过该时间数据不接入,则切分窗口并触发计算。
tableEnv.scan("Sensors")
.window(Session.withGap("10.minutes").on("rowtime").as("w"));
.window(Session.withGap("10.minutes").on("proctime").as("w"));
Over Windows
Over Window
和标准SQL
中提供的Over
语法功能类似,也是一种数据聚合计算的方式,但和Group Window
不同的是,Over Window
不需要对输入数据按照窗口大小进行堆叠。Over Window
是基于当前数据和其周围邻近范围内数据进行聚合统计的,例如基于当前记录前面的20条数据,然后基于这些数据统计某一指标的聚合结果。
在Table API
中,Over Window
也是在window
方法中指定,但后面不需要和groupBy
操作符绑定,后面直接接SELECT
操作符,并在select
操作符中指定需要查询字段和聚合指标。
Table table = input
.window([w: OverWindow] as "w")
.select("a, b.sum over w, c.min over w");
无界Over Windows
-
可以在事件时间或处理时间,以及指定为时间间隔、或行计数的范围内,定义
Over windows
-
无界的
over window
是使用常量指定的
// 无界的事件时间 over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding(UNBOUNDED_RANGE).as("w"))
//无界的处理时间 over window
.window(Over.partitionBy("a").orderBy("proctime").preceding(UNBOUNDED_RANGE).as("w"))
// 无界的事件时间 Row-count over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding(UNBOUNDED_ROW).as("w"))
//无界的处理时间 Row-count over window
.window(Over.partitionBy("a").orderBy("proctime").preceding(UNBOUNDED_ROW).as("w"))
有界Over Windows
- 有界的
over window
是用间隔的大小指定的
// 有界的事件时间 over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding("1.minutes").as("w"))
// 有界的处理时间 over window
.window(Over.partitionBy("a").orderBy("proctime").preceding("1.minutes").as("w"))
// 有界的事件时间 Row-count over window
.window(Over.partitionBy("a").orderBy("rowtime").preceding("10.rows").as("w"))
// 有界的处理时间 Row-count over window
.window(Over.partitionBy("a").orderBy("procime").preceding("10.rows").as("w"))
实际案例
val resultTable = sensorTable
.window(Over partitionBy 'id orderBy 'ts preceding 2.rows as 'ow)
.select('id, 'ts, 'id.count over 'ow, 'temperature.avg over 'ow)
SQL中的Group Windows
Group Windows
定义在 SQL 查询的 Group By
子句中
-
TUMBLE(time_attr, interval)
- 定义一个滚动窗口,第一个参数是时间字段,第二个参数是窗口长度
-
HOP(time_attr, interval, interval)
- 定义一个滑动窗口,第一个参数是时间字段,第二个参数是窗口滑动步长,第三个是 窗口长度
-
SESSION(time_attr, interval)
- 定义一个会话窗口,第一个参数是时间字段,第二个参数是窗口间隔
实现案例
tableEnv.createTemporaryView("sensor", sensorTable)
val resultSqlTable = tableEnv.sqlQuery(
"""
|select
|id,
|count(id),
|avg(temperature),
|tumble_end (ts, interval '10' second)
|from sensor
|group by
|id , tumble(ts, interval '10' second)
|""".stripMargin
)
SQL中的Over Windows
用Over
做窗口聚合时,所有聚合必须在同一窗口上定义,也就是说必须是相同的分区、排序和范围
目前仅支持在当前行范围之前的窗口
ORDER BY
必须在单一的时间属性上指定
SELECT COUNT(amount) OVER (
PARTITION BY user
ORDER BY proctime
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
FROM Orders
实际案例
val resultSqlTable = tableEnv.sqlQuery(
"""
|select id, ts ,count(id) over ow ,avg(temperature) over ow
|from sensor
|window ow as (
| partition by id
| order by ts
| rows between 2 preceding and current row
|)
|""".stripMargin
)