SparkSQL 常用函数(Scala DSL 风格)全总结
SparkSQL 的 DSL(Domain-Specific Language)风格基于 DataFrame/Dataset API 调用函数,无需编写 SQL 字符串,更贴合 Scala 编程习惯。以下按功能分类梳理高频函数,所有函数均需导入 org.apache.spark.sql.functions._(核心包),示例基于 Scala 语法,结合实际业务场景说明。
前置说明
- 示例中统一使用
df: DataFrame代表待操作的数据集; - 函数调用格式为
df.withColumn("新列名", 函数名(参数))(新增列)或df.select(函数名(列名), ...)(查询列); - 列名可用
col("列名")或$"列名"(需导入spark.implicits._),示例中混用两种风格以适配不同习惯。
一、基础类型函数
1. 数值函数(处理整数/浮点数)
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
abs(col) |
绝对值 | df.withColumn("abs_num", abs($"num")) |
round(col, n) |
四舍五入到 n 位小数 | df.withColumn("round_num", round($"num", 2)) |
ceil(col) |
向上取整 | df.withColumn("ceil_num", ceil($"num")) |
floor(col) |
向下取整 | df.withColumn("floor_num", floor($"num")) |
rand() |
生成 0~1 随机数(可传种子) | df.withColumn("rand_num", rand(123)) |
pow(col, n) |
求 col 的 n 次方 | df.withColumn("pow_num", pow($"num", 2)) |
sqrt(col) |
平方根(非负) | df.withColumn("sqrt_num", sqrt($"num")) |
sum(col) |
聚合:求和 | df.agg(sum($"num").alias("sum_num")) |
avg(col) |
聚合:平均值 | df.agg(avg($"num").alias("avg_num")) |
max/min(col) |
聚合:最大值/最小值 | df.agg(max($"num"), min($"num")) |
count(col) |
聚合:非空值计数(count(*) 用 count(lit(1))) | df.agg(count($"num").alias("cnt")) |
countDistinct(col) |
聚合:去重计数 | df.agg(countDistinct($"num").alias("distinct_cnt")) |
2. 字符串函数
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
concat(col1, col2) |
拼接字符串 | df.withColumn("concat_str", concat($"name", $"age")) |
concat_ws(sep, cols) |
按分隔符拼接(自动跳过 null) | df.withColumn("concat_ws_str", concat_ws("-", $"name", $"age")) |
substring(col, start, len) |
截取子串(start 从 1 开始) | df.withColumn("sub_str", substring($"name", 1, 3)) |
upper/lower(col) |
转大写/小写 | df.withColumn("upper_str", upper($"name")) |
trim(col) |
去除首尾空格(ltrim/rtrim 单侧) | df.withColumn("trim_str", trim($"name")) |
length(col) |
字符串长度 | df.withColumn("len_str", length($"name")) |
regexp_replace(col, pat, rep) |
正则替换 | df.withColumn("reg_str", regexp_replace($"phone", "(\\d{3})\\d{4}(\\d{4})", "$1****$2")) |
like(col, pat) |
模糊匹配(% 任意字符,_ 单个字符) | df.filter($"name".like("张%")) |
split(col, sep) |
按分隔符切分字符串为数组 | df.withColumn("split_arr", split($"tags", ",")) |
instr(col, substr) |
查找子串位置(从 1 开始,无则返回 0) | df.withColumn("pos", instr($"name", "小")) |
3. 日期时间函数
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
current_date() |
当前日期(yyyy-MM-dd) | df.withColumn("now_date", current_date()) |
current_timestamp() |
当前时间戳(yyyy-MM-dd HH:mm:ss) | df.withColumn("now_ts", current_timestamp()) |
to_date(col, fmt) |
字符串转日期(指定格式) | df.withColumn("date", to_date($"date_str", "yyyyMMdd")) |
to_timestamp(col, fmt) |
字符串转时间戳 | df.withColumn("ts", to_timestamp($"ts_str", "yyyy-MM-dd HH:mm")) |
date_format(col, fmt) |
日期转指定格式字符串 | df.withColumn("date_fmt", date_format($"date", "yyyy年MM月dd日")) |
datediff(end, start) |
两个日期差值(end - start,单位天) | df.withColumn("diff_days", datediff($"end_date", $"start_date")) |
date_add/date_sub(col, n) |
日期加/减 n 天 | df.withColumn("add_days", date_add($"date", 7)) |
months_between(end, start) |
两个日期月份差(小数) | df.withColumn("diff_mon", months_between($"end_date", $"start_date")) |
add_months(col, n) |
日期加 n 个月 | df.withColumn("add_mon", add_months($"date", 1)) |
year/month/day(col) |
提取日期的年/月/日 | df.withColumn("year", year($"date")) |
hour/minute/second(col) |
提取时间戳的时/分/秒 | df.withColumn("hour", hour($"ts")) |
dayofweek(col) |
星期几(1=周日,2=周一...7=周六) | df.withColumn("weekday", dayofweek($"date")) |
dayofyear(col) |
一年中的第几天 | df.withColumn("day_of_year", dayofyear($"date")) |
trunc(col, fmt) |
日期截断(fmt:year/month/week 等) | df.withColumn("trunc_year", trunc($"date", "year")) // 截断到年初 |
二、集合函数(数组/Map)
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
array(col1, col2, ...) |
创建数组 | df.withColumn("arr", array($"col1", $"col2")) |
array_contains(col, val) |
判断数组是否包含指定值 | df.filter(array_contains($"tags_arr", "java")) |
size(col) |
数组/Map 长度 | df.withColumn("arr_len", size($"tags_arr")) |
explode(col) |
炸裂数组(一行转多行) | df.select($"name", explode($"tags_arr").alias("tag")) |
explode_outer(col) |
炸裂数组(保留 null 行) | df.select($"name", explode_outer($"tags_arr").alias("tag")) |
map(key1, val1, key2, val2) |
创建 Map 类型 | df.withColumn("map_col", map($"k1", $"v1", $"k2", $"v2")) |
map_keys(col) |
提取 Map 的所有 key 为数组 | df.withColumn("map_keys", map_keys($"map_col")) |
map_values(col) |
提取 Map 的所有 value 为数组 | df.withColumn("map_vals", map_values($"map_col")) |
element_at(col, idx/key) |
获取数组指定索引元素/Map 指定 key 的值 | df.withColumn("arr_elem", element_at($"tags_arr", 1)) |
concat_ws(sep, col) |
数组转字符串(按分隔符拼接) | df.withColumn("arr_str", concat_ws(",", $"tags_arr")) |
三、条件判断函数
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
when(cond, val).otherwise(val) |
单条件判断(if-else) | df.withColumn("grade", when($"score" >= 90, "A").otherwise("B")) |
case_when(cond1 -> val1, cond2 -> val2, ...) |
多条件判断(switch) | df.withColumn("level", case_when($"score" >= 90 -> "优", $"score" >= 80 -> "良", lit(true) -> "差")) |
coalesce(col1, col2, ...) |
返回第一个非 null 值 | df.withColumn("new_col", coalesce($"col1", $"col2", lit(0))) |
isNull/isNotNull(col) |
判断列是否为 null/非 null | df.filter($"name".isNotNull) |
isnan(col) |
判断数值是否为 NaN | df.filter(isnan($"num")) |
nullif(col1, col2) |
若 col1=col2 则返回 null,否则返回 col1 | df.withColumn("null_col", nullif($"col1", $"col2")) |
四、聚合函数(分组/全局)
1. 基础聚合(需配合 groupBy 或 agg)
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
sum(col) |
求和 | df.groupBy($"dept").agg(sum($"salary").alias("total_sal")) |
avg(col) |
平均值 | df.groupBy($"dept").agg(avg($"salary").alias("avg_sal")) |
max/min(col) |
最大/最小值 | df.groupBy($"dept").agg(max($"salary"), min($"salary")) |
count(col) |
非空计数 | df.groupBy($"dept").agg(count($"id").alias("emp_cnt")) |
countDistinct(col) |
去重计数 | df.groupBy($"dept").agg(countDistinct($"job").alias("job_cnt")) |
first/last(col) |
分组内第一条/最后一条值 | df.groupBy($"dept").agg(first($"name").alias("first_emp")) |
2. 高级聚合
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
collect_list(col) |
分组内收集值为数组(保留重复) | df.groupBy($"dept").agg(collect_list($"name").alias("emp_list")) |
collect_set(col) |
分组内收集值为数组(去重) | df.groupBy($"dept").agg(collect_set($"job").alias("job_set")) |
sum_distinct(col) |
去重后求和 | df.agg(sum_distinct($"num").alias("sum_dist")) |
agg(Map(col1 -> func1, col2 -> func2)) |
批量聚合 | df.groupBy($"dept").agg(Map("salary" -> "sum", "age" -> "avg")) |
五、窗口函数(开窗函数)
需先定义窗口规范(Window 类,导入 org.apache.spark.sql.expressions.Window),再结合聚合/排名函数使用。
1. 窗口规范定义
scala
// 按 dept 分区,按 salary 降序排序
val windowSpec = Window.partitionBy($"dept").orderBy($"salary".desc)
// 按 dept 分区,取当前行前后 1 行(行范围)
val windowSpecWithRange = Window.partitionBy($"dept").orderBy($"salary").rowsBetween(-1, 1)
2. 常用窗口函数
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
row_number().over(window) |
分区内行号(连续,不重复) | df.withColumn("rn", row_number().over(windowSpec)) |
rank().over(window) |
排名(跳号,如 1,2,2,4) | df.withColumn("rk", rank().over(windowSpec)) |
dense_rank().over(window) |
密集排名(不跳号,如 1,2,2,3) | df.withColumn("dr", dense_rank().over(windowSpec)) |
lag(col, n, default).over(window) |
取前 n 行值(默认 null) | df.withColumn("prev_sal", lag($"salary", 1, 0).over(windowSpec)) |
lead(col, n, default).over(window) |
取后 n 行值(默认 null) | df.withColumn("next_sal", lead($"salary", 1, 0).over(windowSpec)) |
sum(col).over(window) |
分区内累计求和 | df.withColumn("cum_sum", sum($"salary").over(windowSpec)) |
ntile(n).over(window) |
分区内分桶(均分为 n 组) | df.withColumn("ntile", ntile(3).over(windowSpec)) |
六、其他常用函数
| 函数 | 功能说明 | 示例(Scala DSL) |
|---|---|---|
lit(val) |
创建常量列(值转列) | df.withColumn("const_col", lit(100)) |
col("name")/$"name" |
引用列($"name" 需 spark.implicits._) | df.select(col("name"), $"age") |
cast(col, type) |
类型转换(StringType/IntegerType 等) | df.withColumn("num", $"str_num".cast(IntegerType)) |
when(cond, val).when(cond2, val2).otherwise(val) |
多条件 | df.withColumn("level", when($"score">=90,"A").when($"score">=80,"B").otherwise("C")) |
greatest(col1, col2, ...) |
返回最大值(多列比较) | df.withColumn("max_val", greatest($"col1", $"col2", $"col3")) |
least(col1, col2, ...) |
返回最小值(多列比较) | df.withColumn("min_val", least($"col1", $"col2")) |
hash(col1, col2, ...) |
计算列的哈希值 | df.withColumn("hash_val", hash($"name", $"id")) |
七、核心使用技巧
-
链式调用 :DSL 支持链式操作,简化代码:
scaladf.select($"name", $"age") .filter($"age" > 18) .withColumn("age_plus_10", $"age" + 10) .groupBy($"name") .agg(avg($"age_plus_10").alias("avg_age")) -
列别名 :所有函数结果建议用
alias命名,避免默认列名(如abs(num)列名是abs(num)); -
null 处理 :优先用
coalesce/when...otherwise处理 null 值,避免计算异常; -
隐式转换 :导入
spark.implicits._后,支持$"列名"、数值运算($"a" + $"b")等语法糖。
以上覆盖 SparkSQL DSL(Scala)90%+ 日常开发场景,可根据业务需求组合使用。