好的,UDF、UDTF 和 UDAF 是三种在大数据处理框架(如 Hive、Spark SQL、Flink SQL)中常见的用户自定义函数类型。它们的主要区别在于输入数据的行数、输出数据的行数以及处理逻辑:
| 特性 | UDF (User-Defined Function) | UDTF (User-Defined Table-Generating Function) | UDAF (User-Defined Aggregation Function) |
|---|---|---|---|
| 核心功能 | 对单行输入进行处理,返回单个值。 | 对单行输入进行处理,返回多行多列的结果。 | 对多行输入(一个分组)进行处理,返回单个聚合值。 |
| 输入 | 一行数据中的一个或多个列值。 | 一行数据中的一个或多个列值。 | 多行数据(一个分组内的所有行)。 |
| 输出 | 一个标量值 (Scalar Value)。 | 一个表 (多行,每行可以有多个列)。 | 一个聚合值 (Scalar Value)。 |
| 行为类比 | 类似于编程语言中的普通函数。 f(a, b) -> c | 类似于一个能“炸开”输入并产生新行的函数。 f(a) -> (x, y), (p, q) | 类似于 SQL 中的 SUM(), COUNT(), AVG() 等聚合函数。 f([a1, a2, a3]) -> c |
| 关键特点 | 一对一 (1行输入 -> 1个输出) | 一对多 (1行输入 -> 多行输出) | 多对一 (多行输入 -> 1个输出) |
| 主要用途 | 数据清洗、转换、计算单个值。 | 解析复杂结构(如JSON数组、Map)、行转列(Pivot)、数据展开(Explode)。 | 分组统计、聚合计算(求和、平均、最大值、最小值、自定义聚合逻辑)。 |
| Hive 示例 | SELECT my_udf(name) FROM users; | SELECT my_udtf(json_array) AS item, price FROM logs; (LATERAL VIEW 常一起使用) | SELECT department, my_udaf(salary) FROM employees GROUP BY department; |
| Spark 示例 | spark.udf.register("myUdf", (s: String) => s.toUpperCase) df.select(myUdf($"name")) | 通常实现为扩展 org.apache.spark.sql.api.java.UDF1 并返回 Iterable[Row] 或使用 flatMap。 | 实现 UserDefinedAggregateFunction 或 Aggregator 抽象类/特质。 |
| Flink 示例 | tableEnv.registerFunction("myUdf", new MyScalarFunction()); table.select("myUdf(myField)"); | 实现 TableFunction 接口。 table.joinLateral(call("myUdtf", $("myField")).as("newField1", "newField2")) | 实现 AggregateFunction 接口。 table.groupBy($("key")).aggregate(call("myUdaf", $("value")).as("myResult")).select($("key"), $("myResult")); |
| 通俗比喻 | 给一个人(一行)量身高,输出一个数字(身高值)。 | 打开一个人的背包(一行),把里面的每件物品(多行)都拿出来单独放好。 | 统计一群人的平均身高(多行输入),输出一个平均身高值(一个结果)。 |
详细解释:
-
UDF (User-Defined Function) - 用户定义函数
- 作用: 最常见的自定义函数类型。它对输入的单个行进行操作,读取该行中一个或多个列的值,经过计算或处理后,返回一个单一的结果值。
- 输入/输出: 一行输入 -> 一个标量值输出。
- 类比: 就像数学函数
y = f(x)或者编程语言中的一个方法String upperCase(String input)。 - 典型应用:
- 字符串操作:转换大小写、拼接、替换、提取子串。
- 数学计算:对数、指数、三角函数、自定义公式。
- 数据类型转换:
String转Date,String转Integer。 - 条件逻辑:模拟
CASE WHEN语句的复杂逻辑。
- Hive 示例:
SELECT toUpper(name), my_custom_calculation(salary, bonus) FROM employees; - Spark 示例:
df.withColumn("full_name", concat_udf(col("first_name"), col("last_name")))
-
UDTF (User-Defined Table-Generating Function) - 用户定义表生成函数
- 作用: 它对输入的单个行进行操作,读取该行中一个或多个列的值,然后生成零行、一行或多行输出结果。输出的每行可以包含多个列。它“炸开”或“展开”了输入行。
- 输入/输出: 一行输入 -> 零行、一行或多行输出(构成一个虚拟表)。
- 关键特点: 必须与
LATERAL VIEW(在 Hive 中) 或CROSS JOIN LATERAL TABLE(...)/JOIN LATERAL TABLE(...)(在标准 SQL/Spark/Flink 中) 结合使用,才能将 UDTF 产生的多行结果与原始表的其他列关联起来。 - 典型应用:
- 解析数组(
ARRAY)或映射(MAP)类型的列:将数组中的每个元素或映射中的每个键值对拆分成单独的行。EXPLODE是 Hive/Spark 内置的典型 UDTF。 - 解析复杂结构:如 JSON 字符串、XML 字符串,将其内容解析成多行多列的结构化数据。
- 行转列(Pivoting):虽然 UDTF 本身不是直接做透视表,但可以通过生成多行来辅助实现。
- 数据生成:基于输入参数生成一系列值(如时间序列)。
- 解析数组(
- Hive 示例:
SELECT user_id, word FROM logs LATERAL VIEW explode(split(text, ' ')) words_table AS word; -- explode 是内置 UDTF - Spark 示例: (使用
explode)df.select($"user_id", explode($"hobbies").as("hobby"))
-
UDAF (User-Defined Aggregation Function) - 用户定义聚合函数
- 作用: 它对一组行(通常是一个
GROUP BY分组或窗口内的所有行)进行操作。它接收该组内所有行中某个(或某些)列的值,执行聚合计算(如求和、计数、平均、最大值、最小值或任何自定义逻辑),并最终返回一个单一的聚合结果值。 - 输入/输出: 多行输入(一个分组) -> 一个标量值输出。
- 实现复杂度: 通常是最复杂的一类 UDF,因为它需要管理聚合过程中的中间状态。实现时通常需要定义:
- 一个用于存储中间聚合结果的数据结构(缓冲区)。
- 初始化缓冲区的方法 (
initialize或createAccumulator)。 - 更新缓冲区的方法 (
iterate或accumulate),处理每一行输入。 - 合并两个缓冲区的方法 (
merge),用于分布式计算(如 MapReduce 或 Spark 的 shuffle 阶段)。 - 最终计算并返回结果的方法 (
terminate或getValue)。
- 典型应用:
- 自定义统计指标:计算中位数、众数、分位数、方差、标准差、加权平均、复杂业务指标(如客户生命周期价值)。
- 内置聚合函数的扩展或组合:实现标准 SQL 没有提供的聚合逻辑。
- 复杂数据汇总。
- Hive 示例:
SELECT department, my_custom_avg(salary) FROM employees GROUP BY department; - Spark 示例: (实现
UserDefinedAggregateFunction或更现代的Aggregator)val myUdaf = new MyCustomAverageUdaf() df.groupBy($"department").agg(myUdaf($"salary").as("avg_salary"))
- 作用: 它对一组行(通常是一个
总结:
- 用 UDF 来逐行转换数据,输入一行,输出一个新的值。
- 用 UDTF 来展开或解析单行数据,输入一行,输出零到多行新数据(可能多列)。常与
LATERAL VIEW/JOIN LATERAL联用。 - 用 UDAF 来跨多行聚合数据(分组或窗口),输入多行,输出一个汇总结果。用于
GROUP BY或窗口函数中。
理解这三者的核心区别在于它们处理的数据粒度(单行 vs 多行)和输出的结构(单个值 vs 多行 vs 聚合值),是有效使用它们进行复杂数据处理的关键。
697

被折叠的 条评论
为什么被折叠?



