首页 / 工具 / 迁移清单 / Flink SQL → Doris
Flink SQL 迁移到 Doris 的函数改写清单
从 Flink SQL 迁移到 Doris 时,在 Flink SQL 有对应写法的 43 个常用函数中,21 个需要改写,另有 3 个可以改用 Doris 的原生函数。逐个列出两边写法和注意事项。
需要改写的函数 · 可以改用原生函数 · 迁移注意事项 · 可以直接用的函数 · 反向:Doris 迁移到 Flink SQL
需要改写的函数
| 函数 | Flink SQL 写法 | Doris 写法 | Doris 说明 |
|---|---|---|---|
| ADD_MONTHS | TIMESTAMPADD(MONTH, n, timepoint) | 需改写 MONTHS_ADD(date, nums) | 文档没有列出 ADD_MONTHS 别名。返回类型与输入相同,2020-01-31 加 1 个月得到 2020-02-29。 |
| COLLECT_LIST | ARRAY_AGG(expression) | 支持 COLLECT_LIST(expr[, max_size]) | 别名 GROUP_ARRAY,会丢弃 NULL,max_size 限制数组长度。 |
| COLLECT_SET | ARRAY_AGG(DISTINCT expression) | 支持 COLLECT_SET(expr[, max_size]) | 别名 GROUP_UNIQ_ARRAY。会过滤 NULL,max_size 限制数组长度。 |
| DATE_ADD | TIMESTAMPADD(DAY, n, timepoint) | 支持 DATE_ADD(date, INTERVAL n DAY) | 别名有 days_add 和 adddate。单位支持 YEAR、MONTH、DAY、HOUR、MINUTE、SECOND。 |
| DATE_SUB | TIMESTAMPADD(DAY, -n, timepoint) | 支持 DATE_SUB(date, INTERVAL n DAY) | 别名有 days_sub 和 subdate。单位支持 YEAR、MONTH、DAY、HOUR、MINUTE、SECOND。 |
| DATEDIFF | TIMESTAMPDIFF(DAY, timepoint1, timepoint2) | 支持 DATEDIFF(expr1, expr2) | 结果为 expr1 减 expr2,按天计算。 |
| DATETRUNC | FLOOR(timepoint TO MONTH) | 需改写 DATE_TRUNC(datetime, 'month') | 文档列出两种参数顺序,示例都是日期在前。 |
| EXPLODE | CROSS JOIN UNNEST(arr) AS t(x) | 支持 LATERAL VIEW EXPLODE(arr) t AS x | 通常配合 LATERAL VIEW 使用。EXPLODE 对空数组和 NULL 不输出行,EXPLODE_OUTER 会保留一行 NULL。 |
| FROM_JSON | JSON_VALUE(json, path) / JSON_QUERY(json, path) | 需改写 JSON_PARSE(json_str) | 没有 FROM_JSON。JSON_PARSE 遇到非法 JSON 会报错;希望返回 NULL 时用 JSON_PARSE_ERROR_TO_NULL。取字段用 JSON_EXTRACT 系列函数。 |
| GET_JSON_OBJECT | JSON_VALUE(json, path) | 需改写 GET_JSON_STRING(json_str, json_path) | 路径以 $ 开头,不能包含双引号和方括号字符,含点号的键要用双引号括起来。官方建议改用 jsonb 类型和 jsonb_extract 系列函数。 |
| LAST_DAY | TIMESTAMPADD(DAY, -1, TIMESTAMPADD(MONTH, 1, FLOOR(ts TO MONTH))) | 支持 LAST_DAY(date) | 返回 DATE。 |
| MONTHS_BETWEEN | TIMESTAMPDIFF(MONTH, start, end) | 支持 MONTHS_BETWEEN(end, start[, round_type]) | 与 Oracle 的 MONTHS_BETWEEN 一致。时间部分不参与计算;两个日期同为月末或日相同时返回整数。round_type 为 true 时保留 8 位小数。 |
| NVL | IFNULL(input, null_replacement) | 支持 NVL(expr1, expr2) | NVL 是 IFNULL 的别名。expr1 不为 NULL 时返回 expr1,否则返回 expr2。 |
| PERCENTILE_APPROX | PERCENTILE(expr, percentage[, frequency]) | 支持 PERCENTILE_APPROX(col, p[, compression]) | p 必须是 0~1 的常量。compression 取 2048~10000。只统计非 NULL 值,组内没有有效数据时返回 NULL。 |
| SIZE | CARDINALITY(array_or_map) | 别名 ARRAY_SIZE(arr) / MAP_SIZE(m) | 数组用 ARRAY_SIZE,NULL 元素也计入,输入为 NULL 时返回 NULL。Map 用 MAP_SIZE,NULL 键和值都计入。 |
| SPLIT | SPLIT(string, delimiter) | 需改写 SPLIT_BY_STRING(str, separator) | 返回字符串数组,任一参数为 NULL 时返回 NULL。分隔符为空字符串时按字节拆分。 |
| SPLIT_PART | SPLIT_INDEX(str, delimiter, n - 1) | 支持 SPLIT_PART(str, separator, part_index) | 下标从 1 开始。part_index 为 0 或越界时返回 NULL,任一参数为 NULL 时也返回 NULL。 |
| SUBSTR | SUBSTR(string, integer1[, integer2]) | 支持 SUBSTRING(str, pos[, len]) | 别名 SUBSTR。从 1 开始计数,负数从末尾倒数,pos 为 0 或超出长度时返回空字符串,任一参数为 NULL 时返回 NULL。 |
| TO_DATE | TO_DATE(string1[, string2]) | 需改写 str_to_date(str, format) | Doris 的 TO_DATE 只接受 DATETIME 并取日期部分。按格式解析用 STR_TO_DATE,返回 DATETIME,也接受 yyyyMMdd 等三种格式。 |
| WEEKOFYEAR | WEEK(date) | 支持 WEEKOFYEAR(date) | 周一为一周的开始,一周中有 4 天及以上在当年时算作当年的周,返回 1~53,与 MySQL 的 weekofyear 一致。 |
| WM_CONCAT | LISTAGG(expression[, separator]) | 需改写 GROUP_CONCAT([DISTINCT] str[, sep] [ORDER BY ...]) | 分隔符是第二个参数,默认是逗号加空格。DISTINCT 与 ORDER BY 同时使用从 2.1.6 起支持。 |
可以改用原生函数
下面的函数在 Flink SQL 中只能用替代写法,Doris 有对应的函数。原来的写法通常仍能运行,换成原生函数会更简洁。
| 函数 | Flink SQL 写法 | Doris 写法 | Doris 说明 |
|---|---|---|---|
| ANY_VALUE | FIRST_VALUE(expression) | 支持 ANY_VALUE(expr) | 别名 ANY。组内有非 NULL 值时返回其中一个,全部为 NULL 时返回 NULL。 |
| APPROX_COUNT_DISTINCT | COUNT(DISTINCT expr) | 支持 APPROX_COUNT_DISTINCT(expr) | 别名是 NDV。基于 HyperLogLog,只统计非 NULL 值,返回 BIGINT。 |
| MAX_BY | ROW_NUMBER() OVER (PARTITION BY ... ORDER BY y DESC) = 1 | 支持 MAX_BY(expr1, expr2) | 返回 expr2 最大时对应的 expr1。文档没有说明 NULL 和并列的处理。 |
迁移注意事项
ADD_MONTHS 按月加减
- 月末日期:StarRocks、Doris 和 Spark 在目标月份没有对应日期时取当月最后一天。StarRocks 文档特别说明,2022-02-28 加 1 个月得到 3 月 28 日而不是 3 月 31 日,这和 Oracle 的行为不同。
- Hologres 和 Flink 的月末行为文档没有写明,迁移涉及月末日期时建议用已知数据核对。
COLLECT_LIST 聚合为数组
- StarRocks 的 ARRAY_AGG 保留 NULL,MaxCompute 和 Doris 的 COLLECT_LIST 丢弃 NULL,数组长度可能不一致。
COLLECT_SET 去重聚合为数组
- NULL 的处理不同:MaxCompute、Doris 会丢弃 NULL,StarRocks 的 ARRAY_AGG 会保留 NULL。迁移到 StarRocks 后数组里可能多出 NULL 元素。
- Doris 的 max_size 截断时保留哪些元素,文档没有说明,不要依赖它取“前 N 个”。
DATE_SUB 日期减天数
- StarRocks 和 Doris 的 DATE_SUB 文档只列出 INTERVAL 写法。把 Hive 的 date_sub(d, 1) 搬到 StarRocks 时,改成 DATE_SUB(d, INTERVAL 1 DAY) 或 days_sub(d, 1)。
DATEDIFF 日期差
- MaxCompute、StarRocks、Doris、Hive 和 Spark 都是结束日期在前。Flink 的 TIMESTAMPDIFF 先写单位,再写开始时间和结束时间。Flink 文档没有写明正负号的方向,按标准 SQL 的约定结果为 timepoint2 减 timepoint1,迁移后建议用一条已知数据核对符号。
DATETRUNC 日期截断
- 参数顺序不同:MaxCompute 是日期在前,Hologres、StarRocks 和 Spark 是单位在前,Doris 两种都接受。
- 周的起点: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 开始,迁移时要调整下标。
FROM_JSON 按 schema 解析 JSON
- StarRocks 的 parse_json 和 Doris 的 JSON_PARSE 返回的是 JSON 类型,下游需要 STRUCT 或 MAP 时要再做一次转换,不能直接替换 from_json。
- 非法 JSON 的处理不同:StarRocks 的 parse_json 返回 NULL,Doris 的 JSON_PARSE 直接报错。脏数据较多的表,在 Doris 中改用 JSON_PARSE_ERROR_TO_NULL。
GET_JSON_OBJECT 提取 JSON 字段
- StarRocks 和 Doris 返回字符串,取出的数字需要再做 CAST。Doris 中值无法无损转换为字符串时返回 NULL。
LAST_DAY 当月最后一天
- 返回类型不同:MaxCompute(不传 date_part)和 Hive 返回字符串,StarRocks 和 Doris 返回 DATE。结果用于关联 DATE 字段时要先转换。
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 元素个数
- StarRocks 和 Doris 区分数组和 Map 函数。从 MaxCompute 或 Hive 迁移时,要按字段类型分别改成 array_length、map_size 或 ARRAY_SIZE、MAP_SIZE。
- StarRocks 和 Doris 文档写明 NULL 元素也计入个数。需要只统计非空元素时,要先过滤再计数。
SPLIT 字符串拆分
- 只需要取某一段时,Hologres 的 SPLIT_PART 和 Flink 的 SPLIT_INDEX 更直接。注意 SPLIT_PART 的序号从 1 开始,SPLIT_INDEX 为负数时返回 NULL。
SPLIT_PART 按分隔符取第 N 段
- 下标越界时结果不同:MaxCompute、StarRocks 和 Spark 返回空字符串,Hologres 和 Doris 返回 NULL。下游用 IS NULL 或 = '' 判断时,迁移后结果可能变化。
- Hive 的数组下标和 Flink 的 SPLIT_INDEX 都从 0 开始。从 SPLIT_PART 改写时要把下标减 1。
SUBSTR 截取子串
- 起始位置写 0 是常见错误。MaxCompute 默认、StarRocks 和 Doris 都返回空字符串,不会报错,结果却全是空值,很难发现。MaxCompute 开启 Hive 兼容模式后又会按 1 处理,同一条 SQL 在不同模式下结果不同。
TO_DATE 字符串转日期
- 同名不同义:把 MaxCompute 的 TO_DATE('20261013', 'yyyymmdd') 直接搬到 StarRocks、Doris 或 Hive 会报错或得到错误结果,需要改用 str_to_date 或 unix_timestamp。
- MaxCompute 的格式符里 mm 是月、mi 是分钟;Spark 和 Flink 里 MM 是月、mm 是分钟。
WM_CONCAT 分组拼接字符串
- 分隔符的位置相反:WM_CONCAT 写在第一个参数,STRING_AGG、GROUP_CONCAT(Doris)和 LISTAGG 写在最后。直接替换函数名会把分隔符当成要拼接的值。
- 不写排序时,各引擎拼接的顺序都不固定。需要稳定结果时,MaxCompute 写 WITHIN GROUP (ORDER BY ...),StarRocks 和 Doris 在 GROUP_CONCAT 内写 ORDER BY。
ANY_VALUE 取组内任意值
- NULL 的处理不同:MaxCompute 和 Doris 会优先返回非 NULL 值,Spark 默认可能返回 NULL,需要传 true 才会跳过 NULL。
APPROX_COUNT_DISTINCT 近似去重计数
- 改写成 Hive 或 Flink 的 COUNT(DISTINCT) 后结果是精确值,但资源消耗会明显变大,大数据量下要评估内存和运行时间。
可以直接用的函数
下面 19 个函数在两个引擎中写法相同。参数、返回值和边界情况仍可能不同,点开函数页查看细节。