Flink 系列第20篇:Flink SQL 语法全解:从 DDL 到 DML,窗口、聚合、列转行一网打尽

一、DDL:构建你的数据世界

在 Flink SQL 中,一切计算的起点都是 CREATE 语句。它负责向当前或指定的 Catalog 中注册库、表、视图或函数。你可以像在传统数据库中一样,使用 CREATE DATABASECREATE TABLECREATE VIEWCREATE FUNCTION 来搭建自己的元数据体系。

1.1 创建表:CREATE TABLE

建表语句是连接外部系统并定义表结构的核心手段。如果同名表已经在 Catalog 中存在,则无法注册,除非使用 IF NOT EXISTS

1.1.1 建表语法骨架
sql 复制代码
CREATE TABLE [IF NOT EXISTS] [catalog_name.][db_name.]table_name
  (
    { <physical_column_definition> |   -- 物理列
      <metadata_column_definition> |   -- 元数据列
      <computed_column_definition>     -- 计算列
    }[ , ...n]
    [ <watermark_definition> ]         -- Watermark
    [ <table_constraint> ][ , ...n]    -- 约束
  )
  [COMMENT table_comment]
  [PARTITIONED BY (partition_column_name1, partition_column_name2, ...)]
  WITH (key1=val1, key2=val2, ...)
  [ LIKE source_table [( <like_options> )] ]

仔细拆解,这个框架包含了 Flink 表定义的几乎所有能力:

  • 物理列:实际存储在外部系统中的字段。
  • 元数据列 :从连接器自身元数据中提取的虚拟字段,如 Kafka 的 offsettimestamp
  • 计算列:基于其他列或常量通过表达式计算出的虚拟列,常用于定义时间属性。
  • Watermark:为事件时间提供进度指示。
  • 约束 :目前主要支持 PRIMARY KEY,但 NOT ENFORCED 表示 Flink 不校验唯一性,仅用于优化器和连接器的语义推导。
  • WITH 子句:连接外部系统时的配置参数。
  • LIKE 子句:基于已有表的 Schema 创建新表,支持灵活地包含或排除约束、分区、生成列、Watermark 等。
1.1.2 三种列类型详解

物理列 是最常见的列,定义了介质中实际存储字段的名称和类型。语法格式为:

复制代码
column_name column_type [ <column_constraint> ] [COMMENT column_comment]

元数据列 通过 METADATA 关键字标识,允许你直接读取数据源自带的元信息。例如,从 Kafka 中读取消息的偏移量和写入时间戳:

sql 复制代码
CREATE TABLE kafka_source (
  user_id BIGINT,
  message STRING,
  offset BIGINT METADATA FROM 'offset' VIRTUAL,   -- 只读虚拟列,不写入Sink
  ts TIMESTAMP(3) METADATA FROM 'timestamp'        -- 可写元数据列
) WITH (
  'connector' = 'kafka',
  ...
);
  • FROM metadata_key:指定连接器提供的元数据键,如果自定义列名与元数据键相同,则 FROM 可省略。
  • VIRTUAL 关键字表示该列为纯逻辑字段 ,不会物化到 Sink 中,常用于条件过滤或时间提取。如果不带 VIRTUAL,则默认是可读可写 的,会被输出到外部系统(前提是 Sink 支持)。为增强可读性,建议在可写时显式声明 NOT VIRTUAL

注意:某些元数据天生只读,例如 Kafka 的 offset 就不能被写入,否则会报错。

计算列 是一个虚拟列,定义时使用 AS 加表达式。它可以包含其他列、常量、函数,但不能写子查询 。计算列最常见的用途就是定义时间属性,比如将字符串或长整型时间戳转换为 TIMESTAMP_LTZ

sql 复制代码
CREATE TABLE events (
  ts_ms BIGINT,
  ts AS TO_TIMESTAMP_LTZ(ts_ms, 3),
  WATERMARK FOR ts AS ts - INTERVAL '5' SECOND
);
1.1.3 Watermark 定义:事件时间的节拍器

只有定义了 Watermark,Flink 才知道事件时间字段如何推进。语法为:

sql 复制代码
WATERMARK FOR rowtime_column_name AS watermark_strategy_expression
  • rowtime_column_name 必须为 TIMESTAMP(3)TIMESTAMP_LTZ(3) 类型。
  • watermark_strategy_expression 通常是 rowtime_column_name - INTERVAL 'max_delay'

Flink SQL 提供了三种内置的 Watermark 生成策略:

  1. 有界无序 (最常用):WATERMARK FOR ts AS ts - INTERVAL '5' SECOND,允许 5 秒的最大乱序。
  2. 严格升序 (几乎不用):WATERMARK FOR ts AS ts,假设时间戳严格递增,一旦出现相等或更小值就视为迟到。
  3. 递增 (一般不用):WATERMARK FOR ts AS ts - INTERVAL '0.001' SECOND,允许相同时间戳但不允许更小。

Watermark 的发出间隔可通过参数 pipeline.auto-watermark-interval 控制,默认 200ms。

1.1.4 主键约束
sql 复制代码
PRIMARY KEY (column_name[, ...]) NOT ENFORCED
  • 主要用于 Upsert 型连接器(如 JDBC、Elasticsearch),让 Flink 知道哪些字段可唯一标识一行。
  • NOT ENFORCED 意味着 Flink 不会去校验数据的唯一性,仅仅作为元数据传递给优化器和 Sink。
  • 主键列不允许为 NULL
1.1.5 WITH 子句参数

WITH 子句以键值对的形式定义连接器及其属性,是连接外部系统的"万能接口"。

参数 说明 示例
connector 连接器类型 'kafka', 'jdbc', 'filesystem'
format 数据格式 'json', 'csv', 'avro'
scan.startup.mode 源表消费起始模式 'earliest-offset', 'latest-offset'
sink.parallelism Sink 并行度 '2'
update-mode 更新模式 'append', 'upsert'

各连接器具体的参数请参考 Flink 官方文档的 Connectors 部分。

1.1.6 分区表与 LIKE 子句

Flink 支持类似 Hive 的分区表,主要用于批处理场景,配合文件系统或 Hive 连接器使用:

sql 复制代码
CREATE TABLE partitioned_table (
  user_id BIGINT,
  event STRING
) PARTITIONED BY (dt STRING, hr STRING)
WITH (
  'connector' = 'filesystem',
  'path' = '/data/events',
  'format' = 'parquet'
);

CREATE TABLE ... LIKE 可以方便地复制表定义,并灵活选择继承哪些特性:

sql 复制代码
CREATE TABLE kafka_backup LIKE kafka_source (
  EXCLUDING OPTIONS    -- 不复制原表的连接配置
) WITH (
  'connector' = 'kafka',
  'topic' = 'backup_topic',
  ...
);

INCLUDING / EXCLUDING / OVERWRITING 可控制对 CONSTRAINTSPARTITIONSGENERATED(计算列)、OPTIONSWATERMARKS 等属性的处理。

1.2 创建数据库、视图与函数

数据库CREATE DATABASE [IF NOT EXISTS] [catalog_name.]db_name [COMMENT ...] WITH (key=val, ...),用于逻辑分组。

视图CREATE [TEMPORARY] VIEW [IF NOT EXISTS] view_name AS query_expression。视图是逻辑上的查询封装,不存储数据,可用于简化复杂查询。

函数CREATE [TEMPORARY|TEMPORARY SYSTEM] FUNCTION function_name AS identifier [LANGUAGE JAVA|SCALA|PYTHON]。支持加载 UDF,方便扩展业务逻辑。

1.3 修改与删除

常用的修改删除语句包括:

sql 复制代码
ALTER TABLE my_table RENAME TO new_table_name;
DROP TABLE IF EXISTS my_table;
DROP VIEW my_view;
DROP DATABASE my_db;

这些语句与标准 SQL 基本一致。


二、DML:流式查询的艺术

2.1 WITH 语句:给复杂查询起个别名

WITH 子句(CTE,Common Table Expression)允许在查询中定义临时结果集,极大地提升了复杂 SQL 的可读性和维护性。语法如下:

sql 复制代码
WITH [RECURSIVE] cte_name [ (column_name [, ...]) ] AS (
  subquery
)
[, ...]
main_query

多 CTE 串联是最常见的用法,例如先定义高价值用户,再关联订单:

sql 复制代码
WITH 
  high_value_users AS (
    SELECT user_id FROM users WHERE credit_score > 800
  ),
  hv_user_orders AS (
    SELECT o.*
    FROM orders o
    JOIN high_value_users hvu ON o.user_id = hvu.user_id
  )
SELECT 
    COUNT(*) AS high_value_orders,
    SUM(amount) AS total_amount
FROM hv_user_orders;

Flink 1.16+ 支持递归 CTE,非常适合处理树形或层次结构数据:

sql 复制代码
WITH RECURSIVE employee_hierarchy AS (
    -- 锚点成员:顶级管理者
    SELECT id, name, manager_id, 1 AS level
    FROM employees WHERE manager_id IS NULL
    UNION ALL
    -- 递归成员:下属
    SELECT e.id, e.name, e.manager_id, eh.level + 1
    FROM employees e
    JOIN employee_hierarchy eh ON e.manager_id = eh.id
)
SELECT * FROM employee_hierarchy;

注意:递归 CTE 必须有终止条件,否则会无限递归;过深的嵌套可能影响可读性;CTE 的作用域仅限于当前查询。

2.2 SELECTDISTINCT 的流式语义

标准 SELECT 骨架如下:

sql 复制代码
SELECT [DISTINCT] select_list
FROM table_expression
[ WHERE boolean_expression ]
[ GROUP BY grouping_expression ]
[ HAVING boolean_expression ]
[ ORDER BY order_expression ]

在无界流上,ORDER BY 需要配合 LIMIT 使用,因为无限流无法对所有数据进行全局排序。

SELECT DISTINCT 在流式任务中隐藏着状态陷阱。我们来看一个例子:

sql 复制代码
INSERT INTO target_table
SELECT DISTINCT id FROM Orders;

这段 SQL 的背后实际经历了:

  1. Source 算子:从 Kafka 持续读取数据,逐条发给下游去重算子。
  2. 去重算子 :维护一个状态,记录已经见过的 id。如果新来的 id 不在状态中,则将其加入状态并输出;如果已存在,则丢弃。
  3. Sink 算子:将去重后的数据写入目标表。

关键点在于,状态会随着不同 id 的数量而无限制增长。为了防止 OOM,可以为状态设置 TTL(存活时间),但这也可能导致长周期后再次出现的 id 被重复输出,破坏去重语义。因此,在实时去重场景中,需要权衡数据完整性与资源消耗。

2.3 时间窗口:用 TVF 玩转四种窗口

Flink SQL 提供了四种时间窗口:滚动窗口(TUMBLE)滑动窗口(HOP)会话窗口(SESSION)累积窗口(CUMULATE) 。从 1.13 版本开始,推荐使用窗口表值函数(Window TVF) 来定义窗口,语法更清晰,并且与标准 SQL 的 GROUP BY 更协调。老式的 GROUP BY TUMBLE(row_time, interval '1' MINUTE) 写法已被标记为废弃。

2.3.1 窗口 TVF 通用语法
sql 复制代码
SELECT col1, agg_func(col2), window_start, window_end
FROM TABLE(窗口函数名(
    TABLE 数据源表,
    DESCRIPTOR(时间列),
    窗口大小参数,
    [其他参数]
))
GROUP BY window_start, window_end, col1;

窗口 TVF 会自动生成三个元数据列:

  • window_start:窗口起始时间(含),类型 TIMESTAMP(3)
  • window_end:窗口结束时间(不含),类型 TIMESTAMP(3)
  • window_time:窗口内最大有效时间戳,等于 window_end - 1 毫秒,类型 TIMESTAMP_LTZ(3)。它的核心用途是时态表关联(Temporal Join),确保关联到窗口关闭之前的维表版本,避免拿错下一窗口的数据。

GROUP BY 子句必须包含至少一个窗口元数据字段(通常是 window_startwindow_end)以及业务分组键。

2.3.2 滚动窗口(TUMBLE)

固定大小,不重叠。每条数据只属于一个窗口。

sql 复制代码
-- 数据源表
CREATE TABLE source_table (
    dim STRING,
    user_id BIGINT,
    price BIGINT,
    row_time AS CAST(CURRENT_TIMESTAMP AS TIMESTAMP(3)),
    WATERMARK FOR row_time AS row_time - INTERVAL '5' SECOND
) WITH (...);

-- 聚合查询
INSERT INTO sink_table
SELECT 
    dim,
    UNIX_TIMESTAMP(CAST(window_start AS STRING)) * 1000 AS window_start,
    COUNT(*) AS pv,
    SUM(price) AS sum_price,
    MAX(price) AS max_price,
    MIN(price) AS min_price,
    COUNT(DISTINCT user_id) AS uv
FROM TABLE(TUMBLE(
    TABLE source_table,
    DESCRIPTOR(row_time),
    INTERVAL '60' SECOND
))
GROUP BY window_start, window_end, dim;

流式语义分析

  • Source 逐条发送数据给窗口聚合算子。
  • 窗口算子根据事件时间(或处理时间)将数据分配到对应窗口。
  • 对于事件时间,当收到的 Watermark > 窗口结束时间 时,窗口触发计算并输出结果。后续的迟到数据默认会被丢弃。
  • Sink 将计算结果写出。
2.3.3 滑动窗口(HOP)

窗口有大小和滑动步长,若步长 < 窗口大小则窗口重叠,一条数据会落入多个窗口。

sql 复制代码
-- 数据源定义同上,聚合如下:
SELECT 
    dim,
    COUNT(DISTINCT user_id) AS uv
FROM TABLE(HOP(
    TABLE source_table,
    DESCRIPTOR(row_time),
    INTERVAL '1' MINUTES,   -- 滑动步长
    INTERVAL '5' MINUTES    -- 窗口大小
))
GROUP BY window_start, window_end, dim;
2.3.4 会话窗口(SESSION)

没有固定时长,由"会话超时时间"界定。若在 INTERVAL 内没有新数据到达,窗口就关闭。

sql 复制代码
SELECT
  window_start, window_end, user_id,
  COUNT(*) AS events_count
FROM TABLE(SESSION(
    TABLE user_activity,
    DESCRIPTOR(activity_time),
    INTERVAL '30' MINUTES   -- 会话超时
))
GROUP BY window_start, window_end, user_id;
2.3.5 累积窗口(CUMULATE)

它以一个最大窗口为周期,按固定步长"逐步扩张"。例如,步长 5 分钟,最大窗口 15 分钟,则从 00:00 开始会依次产生:
[00:00,00:05)[00:00,00:10)[00:00,00:15) 三个窗口。

sql 复制代码
SELECT 
    window_start, window_end,
    SUM(money) AS sum_money,
    COUNT(DISTINCT user_id) AS uv
FROM TABLE(CUMULATE(
    TABLE source_table,
    DESCRIPTOR(row_time),
    INTERVAL '60' SECOND,   -- 累积步长
    INTERVAL '1' DAY        -- 最大窗口大小
))
GROUP BY window_start, window_end;

累积窗口特别适合"每日零点累计到当前时刻"的统计,例如当日累计销售额、实时大屏等场景。

2.3.6 TVF 与多维分析:Grouping Sets / Rollup / Cube

Window TVF 支持在窗口聚合中直接使用 GROUPING SETSROLLUPCUBE,实现一次查询输出多个维度组合的窗口统计。

sql 复制代码
SELECT
    window_end,
    IF(age IS NULL, 'ALL', age) AS age,
    IF(sex IS NULL, 'ALL', sex) AS sex,
    COUNT(DISTINCT user_id) AS uv
FROM TABLE(CUMULATE(
    TABLE source_table,
    DESCRIPTOR(row_time),
    INTERVAL '5' SECOND,
    INTERVAL '1' DAY
))
GROUP BY window_start, window_end,
    GROUPING SETS (
        (),
        (age),
        (sex),
        (age, sex)
    );

2.4 Group By 聚合:没有窗口的"窗口"

脱离时间窗口,普通的 GROUP BY 在流处理中也极有用,但它的语义与窗口聚合截然不同。

窗口聚合 vs Group By 聚合的核心差异

  • 触发机制 :窗口聚合由 Watermark(事件时间)或系统时钟(处理时间)驱动,结果输出后即为最终值;Group By 聚合由数据到达驱动,来一条数据就立刻计算并输出。
  • 结果持续性 :窗口聚合结果确定后不再变化;Group By 聚合是持续更新的,新数据到来会使旧结果失效,因此会产生撤回流(Retract)
  • 语义一致性:窗口聚合适用于具有时间边界的历史统计;Group By 聚合适合持续维护最新的汇总状态。

举例:用滚动窗口和 Group By 实现每分钟 PV/UV 的比较。

sql 复制代码
-- 滚动窗口聚合
INSERT INTO sink_table
SELECT 
    dim,
    COUNT(*) AS pv,
    COUNT(DISTINCT user_id) AS uv,
    UNIX_TIMESTAMP(CAST(TUMBLE_START(row_time, INTERVAL '1' MINUTE) AS STRING)) * 1000 AS window_start
FROM source_table
GROUP BY dim, TUMBLE(row_time, INTERVAL '1' MINUTE);

-- 普通 Group By 聚合(手动将时间对齐到分钟)
INSERT INTO sink_table
SELECT
    dim,
    COUNT(*) AS pv,
    COUNT(DISTINCT user_id) AS uv,
    CAST((UNIX_TIMESTAMP(CAST(row_time AS STRING))) / 60 AS BIGINT) AS window_start
FROM source_table
GROUP BY dim, CAST((UNIX_TIMESTAMP(CAST(row_time AS STRING))) / 60 AS BIGINT);

离线场景两者结果可以等价;但在流式处理中,窗口聚合输出的是不可变的追加流 ,而 Group By 聚合输出的是可变的撤回流

撤回流(Retract Stream) 的流程:

  1. Source 将数据按 GROUP BY key 的哈希发给下游聚合算子。
  2. 聚合算子到 State 中查找该 key 的历史聚合结果。
    • 若存在旧值:计算新值,更新 State,先下发 - [key, oldValue] 撤回旧结果,再下发 + [key, newValue]
    • 若不存在:直接计算新值并更新 State,下发 + [key, newValue]
  3. Sink 接收到撤回流,需要在支持撤回的存储(如 Kafka 的 upsert 模式或数据库)中执行更新或删除。

Group By 的状态同样会无限增长,务必设置合理 TTL。

与 TVF 类似,普通的 Group By 也支持 GROUPING SETSROLLUPCUBE,例如:

sql 复制代码
SELECT 
    supplier_id, rating, product_id, COUNT(*)
FROM Products
GROUP BY GROUPING SETS (
    (supplier_id, product_id, rating),
    (supplier_id, product_id),
    (supplier_id),
    ()
);

2.5 聚合函数:从基础到高级

Flink SQL 提供了丰富的内置聚合函数,以下分类介绍。

基础聚合
函数 返回值 描述
COUNT(*) BIGINT 计数所有行
COUNT([DISTINCT] col) BIGINT 统计非 NULL 值,可去重
SUM([DISTINCT] col) 与输入同类型 数值求和
AVG(col) DOUBLE 平均值
MIN(col) / MAX(col) 与输入同类型 最小/最大值

所有聚合函数自动忽略 NULL

高级聚合

COLLECT(col):将列的所有值收集为一个 MULTISET(多重集),可用于后续自定义处理。
LISTAGG(col [, delimiter]):将字符串拼接在一起,默认分隔符为 ,,例如 LISTAGG(name, '-')

统计聚合

提供了一系列统计分析函数,如 STDDEV_POPSTDDEV_SAMPVAR_POPVAR_SAMPCORRCOVAR_POPCOVAR_SAMP 以及 REGR_SLOPEREGR_INTERCEPT 等线性回归函数。它们对于传感器数据异常检测、业务指标波动分析等场景非常有用。

近似聚合
  • APPROX_COUNT_DISTINCT(col) / APPROX_DISTINCT(col):基于 HyperLogLog 算法的基数估计,牺牲极少精度换取大幅降低的状态大小,适合去重计数的场景。
  • HISTOGRAM(col):返回一个 MAP,包含值的分布直方图,有助于快速了解数据分布。

2.6 开窗函数(Over 聚合)

Over 聚合不同于 Group By 窗口,它保留每一行数据,并在行的上下文中计算聚合值。语法:

sql 复制代码
SELECT 
  agg_func(col) OVER (
    [PARTITION BY col1[, col2, ...]]
    ORDER BY time_col
    range_definition
  )
FROM ...
  • ORDER BY 必须指定一个时间属性列(事件时间或处理时间)。
  • PARTITION BY 可选,指定分组的粒度,类似 Group By 的 key。
  • range_definition 定义聚合的数据范围,有两种方式:
    • 按行数:ROWS BETWEEN 5 PRECEDING AND CURRENT ROW(当前行及之前 5 行)。
    • 按时间区间:RANGE BETWEEN INTERVAL '1' HOUR PRECEDING AND CURRENT ROW(当前行时间之前 1 小时内的所有行)。

Over 聚合在实时特征工程、滑动累计指标、数据分析场景中十分常见,但要注意状态大小会随窗口范围和基数增大。

2.7 集合操作:UNION、INTERSECT、EXCEPT

Flink SQL 支持标准的集合操作,每个操作都有去重版本和保留所有版本:

  • UNION / UNION ALL
  • INTERSECT / INTERSECT ALL
  • EXCEPT / EXCEPT ALL
  • IN 子查询(仅支持单列)

注意:所有集合操作在流任务中都会产生撤回流。例如左流一条数据先到,但在右流尚未找到匹配时,它会先输出;随后右流对应数据到达后,需要将之前的假阳性结果撤回。这同样需要 Sink 支持 Retract。

此外,IN 子查询内部也维护状态,数量巨大时须设置 TTL。

2.8 ORDER BYLIMIT 在流中的限制

实时任务中,ORDER BY 必须引用升序时间属性 ,即 Watermark 策略为严格升序 (ts AS ts) 或递增 (ts - INTERVAL '0.001' SECOND)。这是因为只有时间单调递增,Flink 才能确定排序结果的完整性,否则面对无限流无法给出全局有序的输出。

LIMIT 通常配合 ORDER BY 使用,比如"过去 1 小时内销售额最高的 10 个商品"。但如果不加窗口约束直接对无限流排序,Flink 将抛出异常。


三、列转行:从一行到多行

当一行数据里包含数组或需要拆分成多行时,列转行技术就派上用场了。

3.1 Array Expansion(数组列转行)

适用于将 ARRAY<STRING> 等数组类型的字段拍平,生成多行。例如日志上报时经常将多个事件打成数组批量上报,分析时需要拆开:

sql 复制代码
CREATE TABLE show_log_table (
    log_id BIGINT,
    show_params ARRAY<STRING>
) WITH (...);
-- 数据:+I[7, [a, b, c]]

SELECT log_id, t.show_param
FROM show_log_table
CROSS JOIN UNNEST(show_params) AS t (show_param);
-- 结果:7 -> a; 7 -> b; 7 -> c

UNNEST 相当于为每个数组元素生成一行,与 CROSS JOIN 配合完成转换。

3.2 Table Function(自定义列转行)

Table Function 本质是 UDTF(用户定义的表函数),逻辑更灵活。你可以像写 Hive UDTF 一样,通过 Java/Scala 定义拆分的规则。

使用方式

  • INNER JOIN:如果 UDTF 返回空结果,那么该行被直接丢弃。
  • LEFT JOIN:UDTF 返回空时,该行保留,但 UDTF 相关字段填充 NULL

例如,定义一个根据 userId 动态生成多行的函数:

java 复制代码
public static class UserProfileTableFunction extends TableFunction<Integer> {
    public void eval(long userId) {
        if (userId <= 5) {
            collect(1);
        } else {
            collect(1);
            collect(2);
            collect(3);
        }
    }
}

注册后在 SQL 中通过 LATERAL TABLE 调用:

sql 复制代码
SELECT user_id, name, t.age, row_time
FROM source_table,
LATERAL TABLE(user_profile_table_func(user_id)) t(age);

这种方式为复杂的一对多转换提供了无限可能。


四、其他常用语句

除了核心的 DDL 和 DML,Flink SQL 还有一批非常实用的辅助语句。

4.1 EXPLAIN:查看执行计划

sql 复制代码
EXPLAIN PLAN FOR <query_statement_or_insert_statement>

它可以显示 SQL 的逻辑计划和经过优化后的物理执行计划,是性能调优和排查问题的一大利器。

4.2 USE:切换上下文

  • USE CATALOG catalog_name:切换 Catalog。
  • USE db_name:切换当前 Database。
  • USE MODULES module_name[, ...]:加载临时 Module(如内置函数扩展)。

4.3 SHOW:列出元数据信息

  • SHOW CATALOGS / SHOW CURRENT CATALOG
  • SHOW DATABASES / SHOW CURRENT DATABASE
  • SHOW TABLES / SHOW VIEWS
  • SHOW FUNCTIONS / SHOW MODULES

这些命令在 SQL 客户端交互式查询时非常方便。

4.4 LOAD / UNLOAD 模块

Module 是 Flink 中用于 UDF 扩展或系统功能增强的机制,通过 LOAD MODULE module_name 可以加载内置或自定义的模块,UNLOAD 则相反。示例:LOAD MODULE hive; 加载 Hive 兼容模块。

4.5 SET / RESET:调整配置

只能在 SQL CLI 中使用。SET 'key' = 'value' 用于修改环境配置,例如 SET 'table.planner' = 'blink';RESET 可以重置指定 key 或所有配置为默认值。这对于临时调整作业行为而不修改 YAML 文件十分便利。

4.6 INSERT:写出数据

这是最常见的输出语句:

sql 复制代码
INSERT INTO target_table [(column1, column2, ...)]
SELECT ... FROM ...;

注意,流式作业中 INSERT INTO 通常是长期运行的,不同于批处理的一次性执行。

4.7 SQL Hints:临时的"魔法棒"

SQL Hints 允许在 DML 语句中通过注释的方式动态覆盖表的配置,非常适合临时调试或特殊场景。

sql 复制代码
SELECT *
FROM kafka_table1 /*+ OPTIONS('scan.startup.mode'='latest-offset') */;

上面的语句会忽略表定义中的 scan.startup.mode 配置,直接使用 latest-offset 进行查询。生产环境中不建议大量使用 Hints,因为会降低可维护性,但它作为临时工具有很强的灵活性。


五、总结

这篇文章系统梳理了 Flink SQL 的语法全景,从 DDL 建表的各种列类型、Watermark 与主键约束,到 DML 中 CTE、窗口 TVF、Group By 与撤回流、聚合函数、Over 聚合、集合操作、列转行,再到 EXPLAIN、SET 等辅助命令。掌握这些语法只是第一步,真正融会贯通还需要深入理解流式状态、时间语义和时区处理。

几个核心要点再强调一下

  • 时间属性是流计算的灵魂,如果涉及事件时间千万别忘了定义 Watermark。
  • 窗口 TVF 是官方推荐的最佳实践,告别老式 GROUP BY TUMBLE 吧。
  • 普通 Group By 会产生撤回流,要根据下游存储的特性谨慎使用。
  • 善用 CTE 提高 SQL 可读性,用列转行处理非结构化数组,用状态 TTL 防止无限膨胀。
相关推荐
jinanwuhuaguo1 小时前
反熵共同体——OpenClaw的宇宙热力学本体论(第十七篇)
大数据·人工智能·安全·架构·kotlin·openclaw
小旭95271 小时前
分布式事务 Seata 详解 + 链路追踪 SkyWalking 实战
java·分布式·后端·信息可视化·skywalking
xjxijd1 小时前
无风扇 AI 服务器成主流:英伟达 NVL72 系统引领静音算力革命
大数据·服务器·人工智能
roman_日积跬步-终至千里2 小时前
【系统架构师案例题-知识点】云原生与大数据架构
大数据·云原生·系统架构
这是程序猿2 小时前
ComfyUI 教程合集|AI绘图、ControlNet、Lora、IPAdapter、视频生成全攻略
大数据·人工智能·windows·音视频
菜鸟小码2 小时前
MapReduce 编程模型详解:Mapper、Reducer、Driver 三大核心组件
大数据·javascript·mapreduce
Gauss松鼠会2 小时前
GaussDB数据库统计信息自动收集机制
数据库·经验分享·sql·oracle·gaussdb
消失的旧时光-19433 小时前
SQL 怎么学(工程实战总纲|用一套用户模型打穿全流程)
java·数据库·sql