您现在的位置是:首页 >学无止境 >【Flink SQL】基本概念网站首页学无止境
【Flink SQL】基本概念
一、时间属性
Event Time
Event Time:您提供的事件时间,通常是数据的最原始的创建时间。
系统会根据数据的Event Time生成的Watermark来进行关窗。只有当Watermark大于关窗时间,才会触发窗口的结束,窗口结束才会输出结果。如果一直没有触发窗口结束的数据流入Flink,则该窗口就无法输出数据。单并发的Watermark是递增的,多并发或者多个源表的Watermark取最小值。
重要
- 如果源表中存在一条未来的乱序数据或者某个并发没有数据,则可能会无法触发窗口结束,从而导致结果数据异常。因此您需要根据数据乱序的程度设置合理的offset大小,并保证所有并发都有数据。如果某个源表并发或源表上游partition因没有数据导致窗口始终无法被触发,则可以在更多Flink配置中添加table.exec.source.idle-timeout: 10s 来触发窗口结束。
- 数据经过GroupBy、双流JOIN或OVER窗口节点后,会导致Watermark属性丢失,无法再使用Event Time进行开窗。
Processing Time
Processing Time:对事件进行处理的本地系统时间。
Processing Time是Flink系统产生的,不在用户的原始数据中。因此需要您显示定义一个Processing Time列。
说明 因为Processing Time容易受到事件到达Flink系统的速度及Flink内部处理数据顺序的影响,所以每次回溯数据的结果可能不一致。
二、水印
watermark直译过来叫水印,但是或许把他翻译成水位线更容易让人理解其含义。
正常情况,数据按照时间顺序到达,但是在分布式系统中,CAP不会被同时满足,数据乱序到达、或者延迟到达的问题再正常不过,因此需要一种机制,能够控制数据处理的速度。
直白点可以这么理解,a、b、c三个系统都是Flink的数据源,三个系统各自独立,网络状态都是不同的。其中有个系统网络状态很是一般,卡的时候能把你卡emo,显然,a、b、c三个系统在同一个时刻产生的数据,到达Flink的时间很可能不同。假设我们现在需要统计10点到10点半间的数据量,那我们该怎么统计呢?
简单呀!来一个统计一个呗,统计到10点31分,我们决定不统计了,但是突然发现一条10点29的数据姗姗来迟。那我们等一会呗,可问题是等到几点?
等到几点这个问题 就是watermark做的事情,他画了一条水位线,过了水位线的咱都不要了。
这就是watermark最直白的含义。
作用
解决分布式数据时间的乱序问题
定义
WATERMARK 定义了表的事件时间属性,其形式为
WATERMARK FOR rowtime_column_name AS watermark_strategy_expression 。
rowtime_column_name 把一个现有的列定义为一个为表标记事件时间的属性。
该列的类型必须为 TIMESTAMP(3),且是 schema 中的顶层列,它也可以是一个计算列。
watermark_strategy_expression 定义了 watermark 的生成策略
它允许使用包括计算列在内的任意非查询表达式来计算 watermark
触发watermark的前提条件
1、表达式的返回类型必须是 TIMESTAMP(3),表示了从 Epoch 以来的经过的时间。
2、返回的 watermark 只有当其不为空且其值大于之前发出的本地 watermark 时才会被发出(以保证 watermark 递增)。
特点
1、每条记录的 watermark 生成表达式计算都会由框架完成。
2、框架会定期发出所生成的最大的 watermark ,如果当前 watermark 仍然与前一个 watermark 相同、为空、或返回的 watermark 的值小于最后一个发出的 watermark ,则新的 watermark 不会被发出。
3、Watermark 根据 pipeline.auto-watermark-interval 中所配置的间隔发出。 若 watermark 的间隔是 0ms ,那么每条记录都会产生一个 watermark,且 watermark 会在不为空并大于上一个发出的 watermark 时发出。
使用事件时间语义时,表必须包含事件时间属性和 watermark 策略。
watermark 策略
严格递增时间戳: WATERMARK FOR rowtime_column AS rowtime_column。
发出到目前为止已观察到的最大时间戳的 watermark ,时间戳大于最大时间戳的行被认为没有迟到。
递增时间戳: WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL ‘0.001’ SECOND。
发出到目前为止已观察到的最大时间戳减 1 的 watermark ,时间戳大于或等于最大时间戳的行被认为没有迟到。
有界乱序时间戳: WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL ‘string’ timeUnit。
发出到目前为止已观察到的最大时间戳减去指定延迟的 watermark ,例如, WATERMARK FOR rowtime_column AS rowtime_column - INTERVAL ‘5’ SECOND 是一个 5 秒延迟的 watermark 策略。
CREATE TABLE Orders (
`user` BIGINT,
product STRING,
order_time TIMESTAMP(3),
WATERMARK FOR order_time AS order_time - INTERVAL '5' SECOND
) WITH ( . . . );
三、窗口函数
作用
Flink SQL窗口函数支持基于无限大窗口的聚合(无需在SQL Query中,显式定义任何窗口)以及对一个特定的窗口的聚合。例如,需要统计在过去的1分钟内有多少用户点击了某个网页,可以通过定义一个窗口来收集最近1分钟内的数据,并对这个窗口内的数据进行实时计算。
分类
Flink SQL支持的窗口聚合主要是两种:Window聚合和Over聚合。本文档主要为您介绍Window聚合。Window聚合支持Event Time和Processing Time两种时间属性定义窗口。每种时间属性类型支持三种窗口类型:滚动窗口(TUMBLE)、滑动窗口(HOP)和会话窗口(SESSION)。
Window聚合
滚动窗口(TUMBLE)、滑动窗口(HOP)和会话窗口(SESSION)
滚动窗口(TUMBLE)
定义
滚动窗口(TUMBLE)将每个元素分配到一个指定大小的窗口中。通常,滚动窗口有一个固定的大小,并且不会出现重叠。例如,如果指定了一个5分钟大小的滚动窗口,无限流的数据会根据时间划分为[0:00, 0:05)
、[0:05, 0:10)
、[0:10, 0:15)
等窗口。
语法
TUMBLE函数用在GROUP BY子句中,用来定义滚动窗口。
TUMBLE(<time-attr>, <size-interval>)
<size-interval>: INTERVAL 'string' timeUnit
说明 <time-attr>
参数必须是时间流中的一个合法的时间属性字段,指定为Processing Time或Event Time,请参见概述,了解如何定义时间属性。
标识函数
使用标识函数选出窗口的起始时间或者结束时间,窗口的时间属性用于下级Window的聚合。
窗口标识函数 | 返回类型 | 描述 |
---|---|---|
TUMBLE_START(time-attr, size-interval) | TIMESTAMP | 返回窗口的起始时间(包含边界)。例如[00:10,00:15) 窗口,返回00:10 。 |
TUMBLE_END(time-attr, size-interval) | TIMESTAMP | 返回窗口的结束时间(包含边界)。例如[00:00, 00:15] 窗口,返回00:15 。 |
TUMBLE_ROWTIME(time-attr, size-interval) | TIMESTAMP(rowtime-attr) | 返回窗口的结束时间(不包含边界)。例如(00:00, 00:15) 窗口,返回00:14:59.999 。返回值是一个rowtime attribute,即可以基于该字段进行时间属性的操作,例如,级联窗口只能用在基于Event Time的Window上,详情请参见级联窗口。 |
TUMBLE_PROCTIME(time-attr, size-interval) | TIMESTAMP(rowtime-attr) | 返回窗口的结束时间(不包含边界)。例如(00:00, 00:15) 窗口,返回00:14:59.999 。返回值是一个Proctime Attribute,即可以基于该字段进行时间属性的操作。例如,级联窗口只能用在基于Processing Time的Window上,详情请参见级联窗口。 |
使用Event Time统计每个用户每分钟在指定网站的单击数示例
测试数据
username(VARCHAR) | click_url(VARCHAR) | eventtime(VARCHAR) |
---|---|---|
Jark | http://taobao.com/xxx | 2017-10-10 10:00:00.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:00:10.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:00:49.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:01:05.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:01:58.0 |
Timo | http://taobao.com/xxx | 2017-10-10 10:02:10.0 |
测试语句
CREATE TEMPORARY TABLE user_clicks(
username varchar,
click_url varchar,
eventtime varchar,
ts AS TO_TIMESTAMP(eventtime),
WATERMARK FOR ts AS ts - INTERVAL '2' SECOND --为Rowtime定义Watermark。
) WITH (
'connector'='sls',
...
);
CREATE TEMPORARY TABLE tumble_output(
window_start TIMESTAMP,
window_end TIMESTAMP,
username VARCHAR,
clicks BIGINT
) WITH (
'connector'='datahub' --目前SLS只支持输出VARCHAR类型的DDL,所以使用DataHub存储。
...
);
INSERT INTO tumble_output
SELECT
TUMBLE_START(ts, INTERVAL '1' MINUTE) as window_start,
TUMBLE_END(ts, INTERVAL '1' MINUTE) as window_end,
username,
COUNT(click_url)
FROM user_clicks
GROUP BY TUMBLE(ts, INTERVAL '1' MINUTE),username;
测试结果
window_start (TIMESTAMP) | window_end (TIMESTAMP) | username(VARCHAR) | clicks(BIGINT) |
---|---|---|---|
2017-10-10 10:00:00.0 | 2017-10-10 10:01:00.0 | Jark | 3 |
2017-10-10 10:01:00.0 | 2017-10-10 10:02:00.0 | Jark | 2 |
2017-10-10 10:02:00.0 | 2017-10-10 10:03:00.0 | Timo | 1 |
使用Processing Time统计每个用户每分钟在指定网站的单击数示例
测试数据
username (VARCHAR) | click_url(VARCHAR) |
---|---|
Jark | http://taobao.com/xxx |
Jark | http://taobao.com/xxx |
Jark | http://taobao.com/xxx |
Jark | http://taobao.com/xxx |
Jark | http://taobao.com/xxx |
Timo | http://taobao.com/xxx |
测试语句
CREATE TEMPORARY TABLE window_test (
username VARCHAR,
click_url VARCHAR,
ts as PROCTIME()
) WITH (
'connector'='sls',
...
);
CREATE TEMPORARY TABLE tumble_output(
window_start TIMESTAMP,
window_end TIMESTAMP,
username VARCHAR,
clicks BIGINT
) WITH (
'connector'='datahub' --目前SLS只支持输出VARCHAR类型的DDL,所以使用DataHub存储。
...
);
INSERT INTO tumble_output
SELECT
TUMBLE_START(ts, INTERVAL '1' MINUTE),
TUMBLE_END(ts, INTERVAL '1' MINUTE),
username,
COUNT(click_url)
FROM window_test
GROUP BY TUMBLE(ts, INTERVAL '1' MINUTE), username;
-
测试结果
window_start (TIMESTAMP) window_end (TIMESTAMP) username (VARCHAR) clicks(BIGINT) 2019-04-11 14:43:00.000
2019-04-11 14:44:00.000
Jark 5 2019-04-11 14:43:00.000
2019-04-11 14:44:00.000
Timo 1
滑动窗口(HOP)
定义
滑动窗口(HOP),也被称作Sliding Window。不同于滚动窗口,滑动窗口的窗口可以重叠。
滑动窗口有两个参数:slide和size。slide为每次滑动的步长,size为窗口的大小。
- slide < size,则窗口会重叠,每个元素会被分配到多个窗口。
- slide = size,则等同于滚动窗口(TUMBLE)。
- slide > size,则为跳跃窗口,窗口之间不重叠且有间隙。
通常,大部分元素符合多个窗口情景,窗口是重叠的。因此,滑动窗口在计算移动平均数(moving averages)时很实用。例如,计算过去5分钟数据的平均值,每10秒钟更新一次,可以设置slide为10秒,size为5分钟。
函数语法
HOP函数用在GROUP BY子句中,用来定义滑动窗口。
HOP(<time-attr>, <slide-interval>,<size-interval>)
<slide-interval>: INTERVAL 'string' timeUnit
<size-interval>: INTERVAL 'string' timeUnit
说明
<time-attr>
参数必须是流中的一个合法的时间属性字段,指定为Processing Time或Event Time。请参见概述,了解如何定义时间属性。
标识函数
使用滑动窗口标识函数选出窗口的起始时间或者结束时间,窗口的时间属性用于下级Window的聚合。
窗口标识函数 | 返回类型 | 描述 |
---|---|---|
HOP_START(<time-attr>, <slide-interval>, <size-interval>) | TIMESTAMP | 返回窗口的起始时间(包含边界)。例如[00:10, 00:15] 窗口,返回00:10 。 |
HOP_END(<time-attr>, <slide-interval>, <size-interval>) | TIMESTAMP | 返回窗口的结束时间(包含边界)。例如[00:00, 00:15] 窗口,返回00:15 。 |
HOP_ROWTIME(<time-attr>, <slide-interval>, <size-interval>) | TIMESTAMP(rowtime-attr) | 返回窗口的结束时间(不包含边界)。例如(00:00, 00:15) 窗口,返回00:14:59.999 。返回值是一个rowtime attribute,即可以基于该字段做时间类型的操作,例如级联窗口,只能用在基于event time的window上。 |
HOP_PROCTIME(<time-attr>, <slide-interval>, <size-interval>) | TIMESTAMP(rowtime-attr) | 返回窗口的结束时间(不包含边界)。例如(00:00, 00:15) 窗口,返回00:14:59.999 。返回值是一个proctime attribute,即可以基于该字段做时间类型的操作,例如级联窗口,只能用在基于processing time的window上。 |
示例
统计每个用户过去1分钟的单击次数,每30秒更新1次,即1分钟的窗口,30秒滑动1次。
-
测试数据
username(VARCHAR) click_url(VARCHAR) ts(TIMESTAMP) Jark http://taobao.com/xxx
2017-10-10 10:00:00.0
Jark http://taobao.com/xxx
2017-10-10 10:00:10.0
Jark http://taobao.com/xxx
2017-10-10 10:00:49.0
Jark http://taobao.com/xxx
2017-10-10 10:01:05.0
Jark http://taobao.com/xxx
2017-10-10 10:01:58.0
Timo http://taobao.com/xxx
2017-10-10 10:02:10.0
测试语句
CREATE TEMPORARY TABLE user_clicks (
username VARCHAR,
click_url VARCHAR,
eventtime VARCHAR,
ts AS TO_TIMESTAMP(eventtime),
WATERMARK FOR ts AS ts - INTERVAL '2' SECOND --为Rowtime定义Watermark。
) WITH (
'connector'='sls',
...
);
CREATE TEMPORARY TABLE hop_output (
window_start TIMESTAMP,
window_end TIMESTAMP,
username VARCHAR,
clicks BIGINT
) WITH (
'connector'='datahub' --目前SLS只支持输出VARCHAR类型的DDL,所以使用DataHub存储。
...
);
INSERT INTO
hop_output
SELECT
HOP_START (ts, INTERVAL '30' SECOND, INTERVAL '1' MINUTE),
HOP_END (ts, INTERVAL '30' SECOND, INTERVAL '1' MINUTE),
username,
COUNT (click_url)
FROM
user_clicks
GROUP BY
HOP (ts, INTERVAL '30' SECOND, INTERVAL '1' MINUTE),username;
测试结果
window_start (TIMESTAMP) | window_end (TIMESTAMP) | username (VARCHAR) | clicks (BIGINT) |
---|---|---|---|
2017-10-10 09:59:30.0 | 2017-10-10 10:00:30.0 | Jark | 2 |
2017-10-10 10:00:00.0 | 2017-10-10 10:01:00.0 | Jark | 3 |
2017-10-10 10:00:30.0 | 2017-10-10 10:01:30.0 | Jark | 2 |
2017-10-10 10:01:00.0 | 2017-10-10 10:02:00.0 | Jark | 2 |
2017-10-10 10:01:30.0 | 2017-10-10 10:02:30.0 | Jark | 1 |
2017-10-10 10:02:00.0 | 2017-10-10 10:03:00.0 | Timo | 1 |
2017-10-10 10:02:30.0 | 2017-10-10 10:03:30.0 | Timo | 1 |
HOP窗口无法读取数据进入的时间,第一个窗口的开启时间会前移。
前移时长=窗口时长-滑动步长
示例如下表。
窗口时长(秒) | 滑动步长(秒) | Event Time | 第一个窗口StartTime | 第一个窗口EndTime |
---|---|---|---|---|
120 | 30 | 2019-07-31 10:00:00.0 | 2019-07-31 09:58:30.0 | 2019-07-31 10:00:30.0 |
60 | 10 | 2019-07-31 10:00:00.0 | 2019-07-31 09:59:10.0 | 2019-07-31 10:00:10.0 |
会话窗口(SESSION)
定义
会话窗口(SESSION)通过SESSION活动来对元素进行分组。会话窗口与滚动窗口和滑动窗口相比,没有窗口重叠,没有固定窗口大小。相反,当它在一个固定的时间周期内不再收到元素,即会话断开时,该窗口就会关闭。
会话窗口通过一个间隔时间(Gap)来配置,这个间隔定义了非活跃周期的长度。例如,一个表示鼠标单击活动的数据流可能具有长时间的空闲时间,并在两段空闲之间散布着高浓度的单击。如果数据在指定的间隔(Gap)之后到达,则会开始一个新的窗口。
函数语法
SESSION函数用于在GROUP BY子句中定义会话窗口。
SESSION(<time-attr>, <gap-interval>)
<gap-interval>: INTERVAL 'string' timeUnit
说明 <time-attr>
参数必须是数据流中的一个合法的时间属性字段,指定为Processing Time或Event Time,详情请参见概述,了解如何定义时间属性。
标识函数
使用标识函数选出窗口的起始时间或者结束时间,窗口的时间属性用于下级Window的聚合。
窗口标识函数 | 返回类型 | 描述 |
---|---|---|
SESSION_START(<time-attr>, <gap-interval>) | Timestamp | 返回窗口的起始时间(包含边界)。例如[00:10,00:15] 的窗口,返回00:10 ,即为此会话窗口内第一条记录的时间。 |
SESSION_END(<time-attr>, <gap-interval>) | Timestamp | 返回窗口的结束时间(包含边界)。例如[00:00,00:15] 的窗口,返回 00:15 ,即为此会话窗口内最后一条记录的时间+<gap-interval> 。 |
SESSION_ROWTIME(<time-attr>, <gap-interval>) | Timestamp(rowtime-attr) | 返回窗口的结束时间(不包含边界)。例如(00:00,00:15) 的窗口,返回00:14:59.999 。返回值是一个rowtime attribute,也就是可以基于该字段进行时间类型的操作,例如级联窗口。该参数只能用于基于Event Time的Window。 |
SESSION_PROCTIME(<time-attr>, <gap-interval>) | Timestamp(rowtime-attr) | 返回窗口的结束时间(不包含边界)。例如(00:00,00:15) 的窗口,返回 00:14:59.999 。返回值是一个Proctime Attribute,也就是可以基于该字段进行时间类型的操作,例如级联窗口。该参数只能用于基于Processing Time的Window。 |
示例
统计每个用户在每个活跃会话期间的单击次数,会话超时时长为30秒。
测试数据
username (VARCHAR) | click_url (VARCHAR) | ts (TIMESTAMP) |
---|---|---|
Jark | http://taobao.com/xxx | 2017-10-10 10:00:00.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:00:10.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:00:49.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:01:05.0 |
Jark | http://taobao.com/xxx | 2017-10-10 10:01:58.0 |
Timo | http://taobao.com/xxx | 2017-10-10 10:02:10.0 |
测试语句
CREATE TEMPORARY TABLE user_clicks(
username varchar,
click_url varchar,
eventtime varchar,
ts AS TO_TIMESTAMP(eventtime),
WATERMARK FOR ts AS ts - INTERVAL '2' SECOND --为Rowtime定义Watermark。
) WITH (
'connector'='sls',
...
);
CREATE TEMPORARY TABLE session_output(
window_start TIMESTAMP,
window_end TIMESTAMP,
username VARCHAR,
clicks BIGINT
) WITH (
'connector'='datahub' --目前SLS只支持输出VARCHAR类型的DDL,所以使用DataHub存储。
...
);
INSERT INTO session_output
SELECT
SESSION_START(ts, INTERVAL '30' SECOND),
SESSION_END(ts, INTERVAL '30' SECOND),
username,
COUNT(click_url)
FROM user_clicks
GROUP BY SESSION(ts, INTERVAL '30' SECOND), username;
测试结果
window_start (TIMESTAMP) | window_end (TIMESTAMP) | username (VARCHAR) | clicks (BIGINT) |
---|---|---|---|
2017-10-10 10:00:00.0 | 2017-10-10 10:00:40.0 | Jark | 2 |
2017-10-10 10:00:49.0 | 2017-10-10 10:01:35.0 | Jark | 2 |
2017-10-10 10:01:58.0 | 2017-10-10 10:02:28.0 | Jark | 1 |
2017-10-10 10:02:10.0 | 2017-10-10 10:02:40.0 | Timo | 1 |
Over聚合
OVER窗口(OVER Window)是传统数据库的标准开窗,不同于Group By Window,OVER窗口中每1个元素都对应1个窗口。OVER窗口可以按照实际元素的行或实际的元素值(时间戳值)确定窗口,因此流数据元素可能分布在多个窗口中。
在应用OVER窗口的流式数据中,每1个元素都对应1个OVER窗口。每1个元素都触发1次数据计算,每个触发计算的元素所确定的行,都是该元素所在窗口的最后1行。在实时计算的底层实现中,OVER窗口的数据进行全局统一管理(数据只存储1份),逻辑上为每1个元素维护1个OVER窗口,为每1个元素进行窗口计算,完成计算后会清除过期的数据。详情请参见Over Aggregation。
语法
SELECT
agg1(col1) OVER (definition1) AS colName,
...
aggN(colN) OVER (definition1) AS colNameN
FROM Tab1;
- agg1(col1):按照GROUP BY指定col1列对输入数据进行聚合计算。
- OVER (definition1):OVER窗口定义。
- AS colName:别名。
说明 - agg1到aggN所对应的OVER definition1必须相同。
- 外层SQL可以通过AS的别名查询数据。
类型
Flink SQL中对OVER窗口的定义遵循标准SQL的定义语法,传统OVER窗口没有对其进行更细粒度的窗口类型命名划分。按照计算行的定义方式,OVER Window可以分为以下两类:
- ROWS OVER Window:每1行元素都被视为新的计算行,即每1行都是一个新的窗口。
- RANGE OVER Window:具有相同时间值的所有元素行视为同一计算行,即具有相同时间值的所有行都是同一个窗口。
属性
正交属性 | 说明 | proctime | eventtime |
---|---|---|---|
ROWS OVER Window | 按照实际元素的行确定窗口。 | 支持 | 支持 |
RANGE OVER Window | 按照实际的元素值(时间戳值)确定窗口。 | 支持 | 支持 |
Rows OVER Window语义
窗口数据
ROWS OVER Window的每个元素都确定一个窗口。
窗口语法
SELECT
agg1(col1) OVER(
[PARTITION BY (value_expression1,..., value_expressionN)]
ORDER BY timeCol
ROWS
BETWEEN (UNBOUNDED | rowCount) PRECEDING AND CURRENT ROW) AS colName, ...
FROM Tab1;
- value_expression:分区值表达式。
- timeCol:元素排序的时间字段。
- rowCount:定义根据当前行开始向前追溯几行元素。
案例
以Bounded ROWS OVER Window场景为例。假设,一张商品上架表,包含有商品ID、商品类型、商品上架时间、商品价格数据。要求输出在当前商品上架之前同类的3个商品中的最高价格。
测试数据
商品ID | 商品类型 | 上架时间 | 销售价格 |
---|---|---|---|
ITEM001 | Electronic | 2017-11-11 10:01:00 | 20 |
ITEM002 | Electronic | 2017-11-11 10:02:00 | 50 |
ITEM003 | Electronic | 2017-11-11 10:03:00 | 30 |
ITEM004 | Electronic | 2017-11-11 10:03:00 | 60 |
ITEM005 | Electronic | 2017-11-11 10:05:00 | 40 |
ITEM006 | Electronic | 2017-11-11 10:06:00 | 20 |
ITEM007 | Electronic | 2017-11-11 10:07:00 | 70 |
ITEM008 | Clothes | 2017-11-11 10:08:00 | 20 |
测试代码
CREATE TEMPORARY TABLE tmall_item(
itemID VARCHAR,
itemType VARCHAR,
eventtime varchar,
onSellTime AS TO_TIMESTAMP(eventtime),
price DOUBLE,
WATERMARK FOR onSellTime AS onSellTime - INTERVAL '0' SECOND --为Rowtime定义Watermark。
) WITH (
'connector' = 'sls',
...
);
SELECT
itemID,
itemType,
onSellTime,
price,
MAX(price) OVER (
PARTITION BY itemType
ORDER BY onSellTime
ROWS BETWEEN 2 preceding AND CURRENT ROW) AS maxPrice
FROM tmall_item;
测试结果
itemID | itemType | onSellTime | price | maxPrice |
---|---|---|---|---|
ITEM001 | Electronic | 2017-11-11 10:01:00 | 20 | 20 |
ITEM002 | Electronic | 2017-11-11 10:02:00 | 50 | 50 |
ITEM003 | Electronic | 2017-11-11 10:03:00 | 30 | 50 |
ITEM004 | Electronic | 2017-11-11 10:03:00 | 60 | 60 |
ITEM005 | Electronic | 2017-11-11 10:05:00 | 40 | 60 |
ITEM006 | Electronic | 2017-11-11 10:06:00 | 20 | 60 |
ITEM007 | Electronic | 2017-11-11 10:07:00 | 70 | 70 |
ITEM008 | Clothes | 2017-11-11 10:08:00 | 20 | 20 |
RANGE OVER Window语义
窗口数据
RANGE OVER Window所有具有共同元素值(元素时间戳)的元素行确定一个窗口。
窗口语法
SELECT
agg1(col1) OVER(
[PARTITION BY (value_expression1,..., value_expressionN)]
ORDER BY timeCol
RANGE
BETWEEN (UNBOUNDED | timeInterval) PRECEDING AND CURRENT ROW) AS colName,
...
FROM Tab1;
- value_expression:进行分区的字表达式。
- timeCol:元素排序的时间字段。
- timeInterval:定义根据当前行开始向前追溯指定时间的元素行。
案例
Bounded RANGE OVER Window场景示例:假设一张商品上架表,包含有商品ID、商品类型、商品上架时间、商品价格数据。需要求比当前商品上架时间早2分钟的同类商品中的最高价格。
测试数据
商品ID | 商品类型 | 上架时间 | 销售价格 |
---|---|---|---|
ITEM001 | Electronic | 2017-11-11 10:01:00 | 20 |
ITEM002 | Electronic | 2017-11-11 10:02:00 | 50 |
ITEM003 | Electronic | 2017-11-11 10:03:00 | 30 |
ITEM004 | Electronic | 2017-11-11 10:03:00 | 60 |
ITEM005 | Electronic | 2017-11-11 10:05:00 | 40 |
ITEM006 | Electronic | 2017-11-11 10:06:00 | 20 |
ITEM007 | Electronic | 2017-11-11 10:07:00 | 70 |
ITEM008 | Clothes | 2017-11-11 10:08:00 | 20 |
测试代码
CREATE TEMPORARY TABLE tmall_item(
itemID VARCHAR,
itemType VARCHAR,
eventtime varchar,
onSellTime AS TO_TIMESTAMP(eventtime),
price DOUBLE,
WATERMARK FOR onSellTime AS onSellTime - INTERVAL '0' SECOND --为Rowtime定义Watermark。
) WITH (
'connector' = 'sls',
...
);
SELECT
itemID,
itemType,
onSellTime,
price,
MAX(price) OVER (
PARTITION BY itemType
ORDER BY onSellTime
RANGE BETWEEN INTERVAL '2' MINUTE preceding AND CURRENT ROW) AS maxPrice
FROM tmall_item;
测试结果
itemID | itemType | onSellTime | price | maxPrice |
---|---|---|---|---|
ITEM001 | Electronic | 2017-11-11 10:01:00 | 20 | 20 |
ITEM002 | Electronic | 2017-11-11 10:02:00 | 50 | 50 |
ITEM003 | Electronic | 2017-11-11 10:03:00 | 30 | 50 |
ITEM004 | Electronic | 2017-11-11 10:03:00 | 60 | 60 |
ITEM005 | Electronic | 2017-11-11 10:05:00 | 40 | 60 |
ITEM006 | Electronic | 2017-11-11 10:06:00 | 20 | 40 |
ITEM007 | Electronic | 2017-11-11 10:07:00 | 70 | 70 |
ITEM008 | Clothes | 2017-11-11 10:08:00 | 20 | 20 |