一、支持的数据类型
VARCHAR 可变长度字符串 VARCHAR最大容量为4MB。
BOOLEAN 逻辑值 取值为TRUE、FALSE或UNKNOWN。
TINYINT 微整型,1字节整数。 -128~127
SMALLINT 短整型,2字节整数。 -32768~32767
INT 整型,4字节整数。 -2147483648~2147483647
BIGINT 长整型,8字节整数。 -9223372036854775808~9223372036854775807
FLOAT 4字节浮点型 6位数字精度
DECIMAL 小数类型 示例:123.45是DECIMAL(5,2)的值。
DOUBLE 浮点型,8字节浮点型。 15位十进制精度。
DATE 日期类型 示例:DATE’1969-07-20’
TIME 时间类型 示例:TIME ‘20:17:40’
TIMESTAMP 时间戳,显示日期和时间。 示例:TIMESTAMP ‘1969-07-20 20:17:40’
VARBINARY 二进制数据 byte[]数组。
说明 VARBINARY没有最大容量的限制。
Array 数组 使用[]进行引用
二、语法约束
当前Flink SQL只支持SELECT,FROM,WHERE,UNION,聚合,窗口,流表JOIN以及流流JOIN。
数据不能对Source流做insert into操作。
Sink流不能用来做查询操作。
三、语法定义
INSERT INTO stream_name query;
query:
values
| {
select
| selectWithoutFrom
| query UNION [ ALL ] query
}
orderItem:
expression [ ASC | DESC ]
select:
SELECT
{ * | projectItem [, projectItem ]* }
FROM tableExpression [ JOIN tableExpression ]
[ WHERE booleanExpression ]
[ GROUP BY { groupItem [, groupItem ]* } ]
[ HAVING booleanExpression ]
selectWithoutFrom:
SELECT [ ALL | DISTINCT ]
{ * | projectItem [, projectItem ]* }
projectItem:
expression [ [ AS ] columnAlias ]
| tableAlias . *
tableExpression:
tableReference
tableReference:
tablePrimary
[ [ AS ] alias [ ‘(’ columnAlias [, columnAlias ]* ‘)’ ] ]
tablePrimary:
[ TABLE ] [ [ catalogName . ] schemaName . ] tableName
| LATERAL TABLE ‘(’ functionName ‘(’ expression [, expression ]* ‘)’ ‘)’
| UNNEST ‘(’ expression ‘)’
values:
VALUES expression [, expression ]*
groupItem:
expression
| ‘(’ ‘)’
| ‘(’ expression [, expression ]* ‘)’
| CUBE ‘(’ expression [, expression ]* ‘)’
| ROLLUP ‘(’ expression [, expression ]* ‘)’
| GROUPING SETS ‘(’ groupItem [, groupItem ]* ‘)’
四、Flink SQL 常用算子
SELECT
SELECT 用于从 DataSet/DataStream 中选择数据,用于筛选出某些列。
例:SELECT * FROM Table;// 取出表中的所有列
SELECT name,age FROM Table;// 取出表中 name 和 age 两列
与此同时 SELECT 语句中可以使用函数和别名
例:SELECT word, COUNT(word) FROM table GROUP BY word;
WHERE
WHERE 用于从数据集/流中过滤数据,与 SELECT 一起使用,用于根据某些条件对关系做水平分割,即选择符合条件的记录
例:SELECT name,age FROM Table WHERE name LIKE ‘%小明%’;
SELECT * FROM Table WHERE age = 20;
WHERE 是从原数据中进行过滤,那么在 WHERE 条件中,Flink SQL 同样支持 =、<、>、<>、>=、<=,以及 AND、OR 等表达式的组合,最终满足过滤条件的数据会被选择出来。并且 WHERE 可以结合 IN、NOT IN 联合使用。
例:SELECT name, age
FROM Table
WHERE name IN (SELECT name FROM Table2)
DISTINCT
DISTINCT 用于从数据集/流中去重根据 SELECT 的结果进行去重
例:SELECT DISTINCT name FROM Table;
对于流式查询,计算查询结果所需的 State 可能会无限增长,用户需要自己控制查询的状态范围,以防止状态过大。
GROUP BY
GROUP BY 是对数据进行分组操作。例如我们需要计算成绩明细表中,每个学生的总分。
例:SELECT name, SUM(score) as TotalScore FROM Table GROUP BY name;
UNION 和 UNION ALL
UNION 用于将两个结果集合并起来,要求两个结果集字段完全一致,包括字段类型、字段顺序。不同于 UNION ALL 的是,UNION 会对结果数据去重
例:SELECT * FROM T1 UNION (ALL) SELECT * FROM T2;
JOIN
JOIN 用于把来自两个表的数据联合起来形成结果表,Flink 支持的 JOIN 类型包括:
JOIN - INNER JOIN
LEFT JOIN - LEFT OUTER JOIN
RIGHT JOIN - RIGHT OUTER JOIN
FULL JOIN - FULL OUTER JOIN
这里的 JOIN 的语义和我们在关系型数据库中使用的 JOIN 语义一致。
例:JOIN(将订单表数据和商品表进行关联)
SELECT * FROM Orders INNER JOIN Product ON Orders.productId = Product.id
LEFT JOIN 与 JOIN 的区别是当右表没有与左边相 JOIN 的数据时候,右边对应的字段补 NULL 输出,
RIGHT JOIN 相当于 LEFT JOIN 左右两个表交互一下位置。
FULL JOIN 相当于 RIGHT JOIN 和 LEFT JOIN 之后进行 UNION ALL 操作。
Group Window
根据窗口数据划分的不同,目前 Apache Flink 有如下 3 种 Bounded Window:
Tumble,滚动窗口,窗口数据有固定的大小,窗口数据无叠加;
Hop,滑动窗口,窗口数据有固定大小,并且有固定的窗口重建频率,窗口数据有叠加;
Session,会话窗口,窗口数据没有固定的大小,根据窗口数据活跃程度划分窗口,窗口数据无叠加。
Group Window定义在GROUP BY里,每个分组只输出一条记录:包括一下几种
分组函数:
TUMBLE(time_attr, interval)
跳跃窗口。
time_attr可以设置processing-time或者event-time。
interval设置窗口周期。
HOP(time_attr, interval, interval)
拓展的跳跃窗口(等价于datastream的滑动窗口),可以分别设置输出触发周期和窗口周期。
SESSION(time_attr, interval)
会话窗口,interval表示多长时间没有记录则关闭窗口。
窗口函数:
TUMBLE_START(time_attr, interval)
返回跳跃窗口开始时间。为UTC时区。
TUMBLE_END(time_attr, interval)
返回跳跃窗口结束时间。为UTC时区。
HOP_START(time_attr, interval, interval)
返回拓展的跳跃窗口开始时间。为UTC时区。
HOP_END(time_attr, interval, interval)
返回拓展的跳跃窗口结束时间。为UTC时区。
SESSION_START(time_attr, interval)
返回会话窗口开始时间。为UTC时区。
SESSION_END(time_attr, interval)
返回会话窗口结束时间。为UTC时区。
Tumble Window
Tumble 滚动窗口有固定大小,窗口数据不重叠,具体语义如下:
Tumble 滚动窗口对应的语法如下:
SELECT
[gk],
[TUMBLE_START(timeCol, size)],
[TUMBLE_END(timeCol, size)],
agg1(col1),
…
aggn(colN)
FROM Tab1
GROUP BY [gk], TUMBLE(timeCol, size)
其中:
[gk] 决定了是否需要按照字段进行聚合;
TUMBLE_START 代表窗口开始时间;
TUMBLE_END 代表窗口结束时间;
timeCol 是流表中表示时间字段;
size 表示窗口的大小,如 秒、分钟、小时、天。
举个例子,假如我们要计算每个人每天的订单量,按照 user 进行聚合分组:
SELECT user, TUMBLE_START(rowtime, INTERVAL ‘1’ DAY) as wStart, SUM(amount) FROM Orders GROUP BY TUMBLE(rowtime, INTERVAL ‘1’ DAY), user;
Hop Window
Hop 滑动窗口和滚动窗口类似,窗口有固定的 size,与滚动窗口不同的是滑动窗口可以通过 slide 参数控制滑动窗口的新建频率。
因此当 slide 值小于窗口 size 的值的时候多个滑动窗口会重叠,具体语义如下:
Hop 滑动窗口对应语法如下:
SELECT
[gk],
[HOP_START(timeCol, slide, size)] ,
[HOP_END(timeCol, slide, size)],
agg1(col1),
…
aggN(colN)
FROM Tab1
GROUP BY [gk], HOP(timeCol, slide, size)
每次字段的意思和 Tumble 窗口类似:
[gk] 决定了是否需要按照字段进行聚合;
HOP_START 表示窗口开始时间;
HOP_END 表示窗口结束时间;
timeCol 表示流表中表示时间字段;
slide 表示每次窗口滑动的大小;
size 表示整个窗口的大小,如 秒、分钟、小时、天。
举例说明,我们要每过一小时计算一次过去 24 小时内每个商品的销量:
SELECT product, SUM(amount) FROM Orders GROUP BY HOP(rowtime, INTERVAL ‘1’ HOUR, INTERVAL ‘1’ DAY), product
Session Window
会话时间窗口没有固定的持续时间,但它们的界限由 interval 不活动时间定义,即如果在定义的间隙期间没有出现事件,则会话窗口关闭。
Seeeion 会话窗口对应语法如下:
SELECT
[gk],
SESSION_START(timeCol, gap) AS winStart,
SESSION_END(timeCol, gap) AS winEnd,
agg1(col1),
…
aggn(colN)
FROM Tab1
GROUP BY [gk], SESSION(timeCol, gap)
[gk] 决定了是否需要按照字段进行聚合;
SESSION_START 表示窗口开始时间;
SESSION_END 表示窗口结束时间;
timeCol 表示流表中表示时间字段;
gap 表示窗口数据非活跃周期的时长。
例如,我们需要计算每个用户访问时间 12 小时内的订单量:
SELECT user, SESSION_START(rowtime, INTERVAL ‘12’ HOUR) AS sStart, SESSION_ROWTIME
TopN 语句
TopN语句用于对实时数据中某个指标的前N个最值的筛选。Flink SQL可以基于OVER窗口操作灵活地完成TopN的功能。
语法:
SELECT * FROM
( SELECT *,
ROW_NUMBER() OVER ([PARTITION BY col1[, col2…]]
ORDER BY col1 [asc|desc][, col2 [asc|desc]…]) AS rownum
FROM table_name)
WHERE rownum <= N [AND conditions]
说明
ROW_NUMBER():行号计算函数OVER的窗口,行号计算从1开始。
PARTITION BY col1[, col2…] :指定分区的列,可以不指定。
ORDER BY col1 [asc|desc][, col2 [asc|desc]…]:指定排序的列和每列的排序方向。
如上语法所示,TopN需要两层Query:
查询中使用 ROW_NUMBER()窗口函数来对数据根据排序列进行排序并标上排名。
外层查询中对排名进行过滤,只取前N条。例如N=10即为取前10条的数据。
WHERE条件的限制
为了使Flink SQL能识别出这是一个TopN的query,外层循环中必须要指定 rownum <= N的格式来指定前N条记录。
此时,您不可以将rownum置于某个表达式中,例如rownum - 5 <= N 。WHERE条件中,可以额外带上其他条件,但是必须使用AND连接条件。
GROUPING SETS
如果您经常需要对数据进行多维度聚合分析(例如既需要按照a列聚合,也要按照b列聚合,同时要按照a和b两列聚合),
您可以使用GROUPING SETS语句进行多维度聚合分析,避免多次使用UNION ALL影响性能
SELECT [ ALL | DISTINCT ]
{ * | projectItem [, projectItem ]* }
FROM tableExpression
GROUP BY
[GROUPING SETS { groupItem [, groupItem ]* } ];
去重语句
去重的方案通常有两种:
保留第一条。
保留最后一条。
说明 ORDER BY后的时间属性字段必须在源表中定义。
由于SQL没有直接去重的语法,因此我们使用SQL的ROW_NUMBER OVER WINDOW功能实现去重。
ROW_NUMBER OVER WINDOW与TopN语句方法类似,可以理解为一种特殊的TopN。
语法:
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER ([PARTITION BY col1[, col2…]
ORDER BY timeAttributeCol [asc|desc]) AS rownum
FROM table_name)
WHERE rownum = 1;
参数说明:
ROW_NUMBER():计算行号,行号计算从1开始。
PARTITION BY col1[, col2…]:指定分区的列,即去重的Key,也可以不指定分区的列。
ORDER BY timeAttributeCol [asc|desc]):指定排序的列,必须是时间属性字段(Processing Time或Event Time)。可以指定为顺序(Keep First Row)或倒序(Keep Last Row)。
外层查询rownum必须为= 1或者<= 1。条件必须是AND,且不能存在Undeterministic的UDF的条件。
如上语法所示,去重需要两层Query:
子查询中:使用ROW_NUMBER(),按照时间属性列对数据进行排序编号。
外层查询中:对排名进行过滤,只取第一条,达到去重的目的。时间列排序方向可以为:
顺序:deduplicate keep first row。
倒序:deduplicate keep last row。
当排序字段是Processing Time列时,Flink会按系统时间去重,其每次运行结果不确定。当排序字段是Event Time列时,Flink会按业务时间去重,其每次运行结果是确定的。
13.1 Deduplicate Keep First Row
保留首行的去重策略,即保留指定Key下第一条出现的数据,之后出现在该Key下的数据会被丢弃掉。因为其State中只存储了Key数据,因此性能较优。示例如下。
语法:
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY b ORDER BY proctime) as rowNum
FROM T
)
WHERE rowNum = 1;
本例中,将T表按照b字段进行去重,并按照系统时间保留第一条数据。proctime在以上示例中是源表T中的一个具有Processing Time属性的字段。
如果您按照系统时间去重,也可以将proctime字段简化成PROCTIME()函数进行调用,可以省略proctime字段的声明。
13.2 Deduplicate Keep Last Row
注意 LastRow不支持使用Event Time进行开窗。
LastRow的作用也是去重,且只保留该主键下最后一条出现的数据。其性能略胜于LAST_VALUE函数,示例如下。
SELECT *
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY b, d ORDER BY proctime DESC) as rowNum
FROM T
)
WHERE rowNum = 1;
14 条件表达式
14.1 CASE表达式
语法:
CASE value WHEN value1 [, value11 ]* THEN result1
[ WHEN valueN [, valueN1 ]* THEN resultN ]* [ ELSE resultZ ]
END
或
CASE WHEN condition1 THEN result1
[ WHEN conditionN THEN resultN ]* [ ELSE resultZ ]
END
语法说明
当value值为value1则返回result1,都不满足则返回resultZ,若没有else语句,则返回null。
当condition1为true时返回result1,都不满足则返回resultZ,若没有else语句,则返回null。
注意事项
所有result的类型都必须一致。
所有condition类型都必须是布尔类型。
当没有满足的分支时,若指定else语句,则返回else的值,若没有else语句,则返回null。
14.2 NULLIF 表达式
语法:
NULLIF(value, value)
语法说明
如果值相同,则返回NULL。 例如,NULLIF(5,5)返回NULL; NULLIF(5,0)返回5。
注意事项
无。
示例
当units等于3时返回null,否则返回units
14.3 COALESCE表达式
语法格式:
COALESCE(value, value [, value ]* )
语法说明
返回从左到右第一个不为NULL的参数的值。
注意事项
所有value的类型都必须一致。
示例:
五、1.11版本的flink新功能
简化 connector 参数
CREATE TABLE user_behavior (
…
) WITH (
‘connector’=‘kafka’,
‘topic’=‘user_behavior’,
‘scan.startup.mode’=‘earliest-offset’,
‘properties.zookeeper.connect’=‘localhost:2181’,
‘properties.bootstrap.servers’=‘localhost:9092’,
‘format’=‘json’
);
内置 connectors
对于想要测试 Flink SQL 性能的用户,可以使用 blackhole 作为 sink;
对于调试排错场景,print sink 会将计算结果打到标准输出(比如集群环境下就会打到 taskmanager.out 文件),使得定位问题的成本大大降低。
在外部 connector 环境还没有 ready 时,用户可以选择 datagen source 和 print sink 快速构建 pipeline 熟悉 Flink SQL;
LIKE 语法优化
Flink SQL 1.11 支持用户从已定义好的 table DDL 中快速 “fork” 自己的版本并进一步修改 watermark 或者 connector 等属性。
比如下面这张 base_table 上想加一个 watermark,在 Flink 1.11 版本之前,用户只能重新将表声明一遍,并加入自己的修改,可谓 “牵一发而动全身”。
从 Flink 1.11 开始,用户只需要使用 CREATE TABLE LIKE 语法就可以完成之前的操作。
六、参考链接
1. https://help.aliyun.com/document_detail/103076.html?spm=a2c4g.11186623.6.688.372e7f6fBRHNRu
2. https://iteblog.blog.csdn.net/article/details/108656414
3. https://zhuanlan.zhihu.com/p/183234177
4. 华为云-flinksqlref-zh.pdf
5. https://support.huaweicloud.com/sqlref-flink-dli/dli_08_0103.html