首页 / 工具 / 迁移清单 / Flink SQL → Spark SQL
Flink SQL 迁移到 Spark SQL 的函数改写清单
从 Flink SQL 迁移到 Spark SQL 时,在 Flink SQL 有对应写法的 43 个常用函数中,23 个需要改写,另有 4 个可以改用 Spark SQL 的原生函数。逐个列出两边写法和注意事项。
需要改写的函数 · 可以改用原生函数 · 迁移注意事项 · 可以直接用的函数 · 反向:Spark SQL 迁移到 Flink SQL
需要改写的函数
| 函数 | Flink SQL 写法 | Spark SQL 写法 | Spark SQL 说明 |
|---|---|---|---|
| ADD_MONTHS | TIMESTAMPADD(MONTH, n, timepoint) | 支持 add_months(start_date, num_months) | Spark 1.5.0 起提供。add_months('2016-08-31', 1) 返回 2016-09-30。 |
| COLLECT_LIST | ARRAY_AGG(expression) | 支持 collect_list(expr) | Spark 2.0.0 起提供,结果顺序不确定。3.3.0 起另有同义的 array_agg。 |
| COLLECT_SET | ARRAY_AGG(DISTINCT expression) | 支持 collect_set(expr) | Spark 2.0.0 起提供。文档注明结果顺序取决于 shuffle 后的行序,是不确定的。 |
| DATE_ADD | TIMESTAMPADD(DAY, n, timepoint) | 支持 date_add(start_date, num_days) | Spark 1.5.0 起提供。3.4.0 起另有同义的 dateadd。 |
| DATE_SUB | TIMESTAMPADD(DAY, -n, timepoint) | 支持 date_sub(start_date, num_days) | Spark 1.5.0 起提供。 |
| DATEADD | TIMESTAMPADD(unit, n, timepoint) | 需改写 ts + INTERVAL n HOUR | Spark 3.4.0 起有 dateadd(start_date, num_days),但只能加天数,没有单位参数。按小时、月等单位加减用 INTERVAL 字面量,按月也可以用 add_months。 |
| DATEDIFF | TIMESTAMPDIFF(DAY, timepoint1, timepoint2) | 支持 datediff(endDate, startDate) | Spark 1.5.0 起提供。3.4.0 起另有同义的 date_diff。 |
| DATETRUNC | FLOOR(timepoint TO MONTH) | 需改写 date_trunc('MONTH', ts) | date_trunc 从 2.3.0 起提供,单位在前,返回 timestamp。只需要日期时可以用 trunc(date, fmt)。 |
| EXPLODE | CROSS JOIN UNNEST(arr) AS t(x) | 支持 explode(expr) | Spark 1.0.0 起提供。数组默认列名为 col,map 为 key 和 value。需要保留空数组的行时用 explode_outer。 |
| FROM_JSON | JSON_VALUE(json, path) / JSON_QUERY(json, path) | 支持 from_json(jsonStr, schema[, options]) | Spark 2.2.0 起提供。schema 用 DDL 字符串,例如 'a INT, b DOUBLE'。options 可以指定 timestampFormat 等解析选项。 |
| GET_JSON_OBJECT | JSON_VALUE(json, path) | 支持 get_json_object(json_txt, path) | 从 Spark 1.5.0 起提供。 |
| GETDATE | CURRENT_TIMESTAMP 或 NOW() | 别名 current_timestamp() 或 now() | 返回查询开始执行时的时间戳,同一查询中各次调用结果相同。3.4.0 起还有不带时区的 localtimestamp()。 |
| LAST_DAY | TIMESTAMPADD(DAY, -1, TIMESTAMPADD(MONTH, 1, FLOOR(ts TO MONTH))) | 支持 last_day(date) | Spark 1.5.0 起提供。 |
| LENGTH | CHAR_LENGTH(string) | 支持 length(expr) | 返回字符数,包含末尾的空格;对二进制数据返回字节数。字节数用 octet_length。 |
| MONTHS_BETWEEN | TIMESTAMPDIFF(MONTH, start, end) | 支持 months_between(timestamp1, timestamp2[, roundOff]) | Spark 1.5.0 起提供。同一天或都是月末时忽略时间部分,否则按每月 31 天计算,默认保留 8 位小数,roundOff 为 false 时不舍入。 |
| NVL | IFNULL(input, null_replacement) | 支持 nvl(expr1, expr2) | Spark 2.0.0 起提供,同时有 ifnull、nvl2 和 coalesce。 |
| PERCENTILE_APPROX | PERCENTILE(expr, percentage[, frequency]) | 支持 percentile_approx(col, percentage[, accuracy]) | Spark 2.1.0 起提供,别名 approx_percentile。accuracy 默认 10000,相对误差为 1.0/accuracy。返回排序后满足条件的最小值,percentage 可以传数组。 |
| SIZE | CARDINALITY(array_or_map) | 支持 size(expr) | Spark 1.5.0 起提供,同时有别名 cardinality。输入为 NULL 时,只有关闭 ANSI 模式并开启 spark.sql.legacy.sizeOfNull 才返回 -1,否则返回 NULL。最新文档写明默认配置下返回 NULL。 |
| SORT_ARRAY | ARRAY_SORT(array[, ascending_order[, null_first]]) | 支持 sort_array(array[, ascendingOrder]) | Spark 1.5.0 起提供。升序时 NULL 在开头,降序时 NULL 在末尾。另有 array_sort,它把 NULL 放在末尾,3.0.0 起支持比较函数。 |
| SPLIT_PART | SPLIT_INDEX(str, delimiter, n - 1) | 支持 split_part(str, delimiter, partNum) | Spark 3.3.0 起提供。下标从 1 开始,负数从末尾数。越界返回空字符串,partNum 为 0 时报错,分隔符为空字符串时不切分。 |
| TO_CHAR | DATE_FORMAT(timestamp, 'yyyy-MM-dd HH:mm:ss') | 支持 to_char(expr, format) | Spark 3.4.0 起提供。4.0 文档写明 expr 可以是日期时间,此时 format 使用 Java 风格格式符;更早的版本请按所用版本文档确认。也可以直接用 date_format。 |
| WEEKOFYEAR | WEEK(date) | 支持 weekofyear(date) | 周一为一周的开始,第一周是第一个超过 3 天的周。 |
| WM_CONCAT | LISTAGG(expression[, separator]) | 需改写 string_agg(expr[, delimiter]) WITHIN GROUP (ORDER BY ...) | string_agg 和 listagg 从 Spark 4.0.0 起提供。更早的版本用 concat_ws(',', collect_list(expr))。 |
可以改用原生函数
下面的函数在 Flink SQL 中只能用替代写法,Spark SQL 有对应的函数。原来的写法通常仍能运行,换成原生函数会更简洁。
| 函数 | Flink SQL 写法 | Spark SQL 写法 | Spark SQL 说明 |
|---|---|---|---|
| ANY_VALUE | FIRST_VALUE(expression) | 支持 any_value(expr[, isIgnoreNull]) | Spark 3.4.0 起提供,结果不确定。isIgnoreNull 为 true 时只返回非 NULL 值,默认可能返回 NULL。 |
| APPROX_COUNT_DISTINCT | COUNT(DISTINCT expr) | 支持 approx_count_distinct(expr[, relativeSD]) | Spark 1.6.0 起提供,基于 HyperLogLog++。relativeSD 是允许的最大相对标准差。 |
| DECODE | CASE expr WHEN search THEN result ... ELSE default END | 支持 decode(expr, search, result [, search, result ] ... [, default]) | 多值匹配写法 3.2.0 起支持。没有匹配且省略 default 时返回 NULL。同名的 decode(bin, charset) 用于按字符集解码。 |
| MAX_BY | ROW_NUMBER() OVER (PARTITION BY ... ORDER BY y DESC) = 1 | 支持 max_by(x, y[, k]) | 传入 k 时返回 y 最大的前 k 个 x 组成的数组,k 最大 100,000。y 全为 NULL 时返回 NULL。 |
迁移注意事项
ADD_MONTHS 按月加减
- 月末日期:StarRocks、Doris 和 Spark 在目标月份没有对应日期时取当月最后一天。StarRocks 文档特别说明,2022-02-28 加 1 个月得到 3 月 28 日而不是 3 月 31 日,这和 Oracle 的行为不同。
- Hologres 和 Flink 的月末行为文档没有写明,迁移涉及月末日期时建议用已知数据核对。
DATEADD 按单位加减日期
- Spark 3.4.0 起的 dateadd(d, 1) 表示加 1 天,参数个数和含义都与 MaxCompute 不同,不能直接照搬。
DATEDIFF 日期差
- MaxCompute、StarRocks、Doris、Hive 和 Spark 都是结束日期在前。Flink 的 TIMESTAMPDIFF 先写单位,再写开始时间和结束时间。Flink 文档没有写明正负号的方向,按标准 SQL 的约定结果为 timepoint2 减 timepoint1,迁移后建议用一条已知数据核对符号。
DATETRUNC 日期截断
- 参数顺序不同:MaxCompute 是日期在前,Hologres、StarRocks 和 Spark 是单位在前,Doris 两种都接受。
- 返回类型不同:MaxCompute 对 DATE 输入返回 DATE,Hologres 和 Spark 的 date_trunc 返回时间戳。结果用于关联 DATE 字段时要先转换。
- 周的起点:MaxCompute 的 week 默认从周一开始,Doris 和 Spark 文档的示例也截断到周一。按周汇总前确认业务定义的周起点。
EXPLODE 数组展开为多行
- 空数组和 NULL 数组的行会被直接丢掉:MaxCompute、Doris 的 EXPLODE 和 StarRocks 的 UNNEST 都是这样。需要保留时,Doris 和 Spark 用 EXPLODE_OUTER,StarRocks 3.2.7 起用 LEFT JOIN UNNEST(...) ON TRUE。
- 需要元素位置时,Hive 和 Spark 的 posexplode 位置从 0 开始,Flink 的 WITH ORDINALITY 从 1 开始,迁移时要调整下标。
GETDATE 获取当前时间
- Flink 流模式下 CURRENT_TIMESTAMP 每条记录都重新求值,同一批数据的结果并不相同。离线 SQL 里依赖“整条 SQL 共用一个时间”的写法,在 Flink 流作业里会出现不同的值。
- 返回类型不同:MaxCompute、StarRocks 和 Doris 返回 DATETIME,Hologres 的 NOW 带时区(文档把不带时区的版本单独列为 LOCALTIMESTAMP),Flink 返回 TIMESTAMP_LTZ(3)。直接写入字符串或 DATETIME 字段时,格式可能变化。
- 精度不同:MaxCompute 默认到秒,StarRocks 和 Doris 默认到秒、可传参数到微秒,Flink 到毫秒。用当前时间做去重或比较时要注意小数部分。
- Hive 和 Spark 的 current_timestamp 可以不带括号,Doris 文档中的写法是 current_timestamp()。迁移时统一写 NOW() 更容易对齐。
LENGTH 字符串长度
- 按字符计数统一写 CHAR_LENGTH 最稳妥:Hologres、StarRocks、Doris、Flink 都支持,Hive 2.2.0 起也支持(char_length 是 character_length 的简写)。
- Spark 的 length 会把末尾空格算进去。从定长 CHAR 列迁移过来的数据要注意补齐的空格。
MONTHS_BETWEEN 相差月数
- Flink 的 TIMESTAMPDIFF(MONTH, t1, t2) 计算的是 t2 减 t1,开始时间在前;MONTHS_BETWEEN 是结束时间在前。照搬参数顺序会得到相反的符号。
- StarRocks 的 months_diff、Flink 的 TIMESTAMPDIFF 和 Hologres 的 EXTRACT 写法都只返回整数月,原 SQL 中依赖小数部分的计算(例如按 0.5 个月判断)需要重新设计。
- 时间部分的处理不同:Doris 只看日期,Hive 会把时间部分算进小数,Spark 在同一天或都是月末时忽略时间。同样的输入可能得到不同的小数。
- 舍入不同:Hive 和 Spark 默认保留 8 位小数,MaxCompute 返回完整的 DOUBLE,Doris 由 round_type 控制。和原结果做等值比较前,先统一 ROUND。
NVL 空值替换
- 迁移到 Hologres、StarRocks 或 Flink SQL 时,统一改写为 COALESCE 最省事,因为 COALESCE 在七个引擎中都可用。
- Flink 的 IFNULL 在第一个参数来自 JSON_VALUE 时可能返回错误结果,问题编号 FLINK-35832。受影响版本包括 1.15~1.20 和 2.0.0,问题已在 2024 年 7 月标记为修复,但没有写明修复版本,使用前请确认所用版本是否包含修复。
PERCENTILE_APPROX 近似分位数
- 第三个参数含义不同:MaxCompute 和 Hive 的 B、Spark 的 accuracy 都默认 10000;StarRocks 和 Doris 的 compression 只接受 2048~10000。从 Spark 迁移时,如果写了大于 10000 的 accuracy,要调整到这个范围内。
- 各引擎的算法不同,结果不会完全一致。MaxCompute 文档写明 p 落在两个值中间时取两者均值,Spark 返回满足条件的最小值,Flink 的 PERCENTILE 用线性插值。P50、P99 这类指标迁移后要实测对比。
- Flink 的 PERCENTILE 是精确计算,每条记录都可能触发全量排序。文档建议用在窗口聚合中,流式分组聚合中慎用。
SIZE 数组和 Map 元素个数
- Spark 对 NULL 输入可能返回 -1,也可能返回 NULL,取决于 ANSI 模式和 spark.sql.legacy.sizeOfNull 两个配置。Spark 3.x 默认关闭 ANSI 模式,所用版本的默认值要按对应版本文档确认。下游有 size(x) = -1 这类判断时,迁移前要先核对配置。
SORT_ARRAY 数组排序
- Spark 有两个排序函数:sort_array 升序时把 NULL 放在开头,array_sort 把 NULL 放在末尾。从 MaxCompute 或 Hive 迁移时应使用 sort_array,结果才一致。
- 降序的写法不同:MaxCompute 和 Spark 传第二个参数,Flink 传 ascending_order,Doris 用 ARRAY_REVERSE_SORT,Hive 和 Hologres 的函数只能升序。
SPLIT_PART 按分隔符取第 N 段
- 下标越界时结果不同:MaxCompute、StarRocks 和 Spark 返回空字符串,Hologres 和 Doris 返回 NULL。下游用 IS NULL 或 = '' 判断时,迁移后结果可能变化。
- Hive 的数组下标和 Flink 的 SPLIT_INDEX 都从 0 开始。从 SPLIT_PART 改写时要把下标减 1。
- 取最后一段时,StarRocks 和 Spark 可以直接传 -1。MaxCompute 要求下标大于 0,不能这样写。
TO_CHAR 日期转字符串
- MaxCompute 的 'yyyy-mm-dd' 原样搬到 Hive、Spark 或 Flink,mm 会被当成分钟,结果类似 2026-30-14,而且不会报错。Java 风格要写 'yyyy-MM-dd'。
ANY_VALUE 取组内任意值
- NULL 的处理不同:MaxCompute 和 Doris 会优先返回非 NULL 值,Spark 默认可能返回 NULL,需要传 true 才会跳过 NULL。
APPROX_COUNT_DISTINCT 近似去重计数
- 改写成 Hive 或 Flink 的 COUNT(DISTINCT) 后结果是精确值,但资源消耗会明显变大,大数据量下要评估内存和运行时间。
- Spark 的第二个参数 relativeSD 在其他引擎中没有对应写法,迁移时要去掉,并按目标引擎的方式调整精度,例如 Hologres 的会话参数。
DECODE 多值匹配
- Hive 和 Flink 也有 DECODE,但含义是字符集解码。把 MaxCompute 的多参数 DECODE 原样搬过去会报参数错误,必须改写成 CASE。
- Spark 3.2 之前只有字符集解码的 decode,多值匹配写法在旧版本中会报错。
可以直接用的函数
下面 16 个函数在两个引擎中写法相同。参数、返回值和边界情况仍可能不同,点开函数页查看细节。