官网参考:Spark SQL, Built-in Functions
aggregate(array, initialValue, mergeFunc, finishFunc) :从初始值开始,遍历数组中的每个元素进行累积计算。
参数说明:
- array:需要处理的数组列
- initialValue:初始值
- mergeFunc:一个Lambda 函数(acc, element) -> newAcc,用于将当前元素合并到累加器
- finishFunc(可选):一个Lambda 函数,用于在遍历完所有元素后对最终结果进行一次转换
sql
select aggregate(array(1,3,5,7,9),2,(arr,x)->arr+x,arr->arr*10);
>> (2+(1+3+5+7+9))*10=270
filter(array, func) :遍历数组中的每个元素,保留满足Lanbda函数条件的元素,返回一个新的数组
sql
-- 保留偶数
select filter(array(1,2,3,4,5), x -> x%2 == 0);
>> [2,4]
在spark2.4之前,没有这些高阶函数的话,要么通过explode+collect_list来实现,需要先将数组拆成多行,处理完在重新聚合成数组。这种方式会引入shuffle操作,且无法保证元素顺序。要么就是编写用户自定义函数来实现。
transform(array, func) :使用Lambda函数转换数组中的元素,返回一个长度相同的新数组。
sql
-- 单参数形式:只处理元素本身
-- 取array struct中的name元素,返回只有name的新array
select transform(array(named_struct('id', 2, 'name', 'N2'), named_struct('id', 1, 'name', 'N1')), x -> x.name);
>> ["N2","N1"]
-- 双参数形式:可同时使用元素和它的索引,索引从0开始
select transform(array(1,3,5), (x,x_index) -> x+x_index);
>> [1,4,7]
array_sort(array,func):对输入数组进行排序。在spark2.4版本只支持按数组第一个元素进行升序排序。在spark3.1版本才支持自定义排序。
sql
-- 对array中的struct进行排序,spark2.4版本默认是按照第一个字段即id进行升序排序,在spark3.1才支持自定义排序
select array_sort(array(named_struct('id', 2, 'name', 'N2'), named_struct('id', 1, 'name', 'N1'))) t;
>> [{"id":1,"name":"N1"},{"id":2,"name":"N2"}]
-- 取array<struct<id:STRING,name:STRING>>中id最小所对应的name
-- 先使用array_sort排序,然后使用transform提取其中的name字段,在使用element_at抓取第一个元素
select element_at(transform(array_sort(array(named_struct('id', 2, 'name', 'N2'), named_struct('id', 1, 'name', 'N1'))),x -> x.name),1);
>> N1
array_distinct(array):从数组中删除重复值。
sql
SELECT array_distinct(array(1, 2, 3, null, 3));
>> [1,2,3,null]
array_except(array1,array2):返回array1中的元素数组,但不包含array2中的元素,且不包含重复项。即数组差集
sql
SELECT array_except(array(1, 2, 3, null, 1), array(1, 3, 5));
>> [2,null]
array_interspect(array1,array2):返回array1和array2相交处的元素数组,不包含重复项。即数组交集
sql
SELECT array_intersect(array(1, 2, 3, null, 1), array(1, 3, 5));
>> [1,3]
array_union(array1,array2):返回array1和array2并集的元素数组,不包含重复项。即数组并集
sql
SELECT array_union(array(1, 2, 3, null, 1), array(1, 3, 5));
>> [1,2,3,null,5]
array_join(array, delimiter[, nullReplacement]) :将数组中的元素用指定的分隔符连接成一个字符串。其中nullReplacement为可选的,用于替换数组中null值的字符串,如果忽略此参数,null值会被直接过滤掉(不出现,也不占位)
sql
SELECT array_join(array('hello', 'world'), ' ');
>> hello world
SELECT array_join(array('hello', null ,'world'), ' ');
>> hello world
SELECT array_join(array('hello', null ,'world'), ' ', ',');
>> hello , world
array_max(array):返回数组中的最大值。对于double/float类型,NaN大于任何非NaN元素。跳过NULL元素。
sql
SELECT array_max(array(1, 20, null, 3));
>> 20
SELECT array_max(array('Hi', 'Hello', 'Hio', null));
>> Hio
array_min(array):返回数组中的最小值。对于double/float类型,NaN大于任何非NaN元素。跳过NULL元素。
sql
SELECT array_min(array(1, 20, null, 3));
>> 1
array_position(array,element):跳过NULL元素,返回数组中第一个匹配元素的(从1开始的)索引,如果找不到匹配项,则返回0。
sql
SELECT array_position(array(1, 3, 5, 1, null), 1);
>> 1
SELECT array_position(array(1, 3, 5, 1, null), 2);
>> 0
SELECT array_position(array(1, 3, 5, 1, null), null);
>> null
array_remove(array,element):从数组中删除所有等于element的元素。
sql
select array_remove(array(1,3,1,2,null), 1);
>> [3,2,null]
array_repeat(element,count):返回包含element重复count次的数组。
sql
select array_repeat('a', 3);
>> ["a","a","a"]
arrays_overlap(a1, a2):如果a1至少包含一个a2中也存在的非空元素,则返回true。如果数组没有公共元素,并且它们都不是空的,并且其中任何一个都包含空元素,则返回null,否则返回false。
sql
SELECT arrays_overlap(array(1, 2, 3, null), array(3, 4, 5));
>> true
SELECT arrays_overlap(array(1, 2, null), array(3, 4, 5));
>> null
SELECT arrays_overlap(array(1, 2), array(3, 4, 5));
>> false
arrays_zip(a1, a2, ...):将多个同索引数组按位置合并为数组结构体(array<struct<0:element1,1:element2,...>>,struct字段名自动为0、1、2....),其中第N个struct包含所有输入数组的第N个值。长度规则:以最长数组为准,短数组缺失位置补NULL。
sql
-- 等长数组
select arrays_zip(array(1,2,3), array('a','b','c'));
>> [{"0":1,"1":"a"},{"0":2,"1":"b"},{"0":3,"1":"c"}]
-- 不等长数组
select arrays_zip(array(1,2), array('a','b','c'));
>> [{"0":1,"1":"a"},{"0":2,"1":"b"},{"0":null,"1":"c"}]
可以结合transform对arrays_zip的结果struct进行重命名
sql
select transform(arrays_zip(array(1,2,3), array('a','b','c')), x -> struct(x.`0` as id, x.`1` as name));
>> [{"id":1,"name":"a"},{"id":2,"name":"b"},{"id":3,"name":"c"}]
或者可以通过explode来实现
sql
select zip_struct.`0` as id, zip_struct.`1` as name
from (
select explode(arrays_zip(array(1,2,3), array('a','b','c'))) as zip_struct
) as tmp;
>>
"id" "name"
"1" "a"
"2" "b"
"3" "c"
element_at(array,index):返回给定(从1开始)索引处的数组元素。如果Index为0,Spark将抛出错误。如果index<0,则从最后一个到第一个访问元素。如果索引超过数组的长度,并且spark.sql.ansi.enabled设置为false,则函数返回NULL。如果spark.sql.ansi.enabled设置为true,则会为无效索引抛出ArrayIndexOutOfBoundsException。
sql
select element_at(array(1,2,3), -1);
>> 3
select element_at(array(1,2,3), 0);
>> java.lang.ArrayIndexOutOfBoundsException
select element_at(array(1,2,3), 5);
>> null
array(1,2,3)[0]也可以从array中取值,与element_at不同的是index是从0开始的,而且不支持index<0的操作
element_at(map,key):返回给定键的值。如果映射中不包含键,则函数返回NULL。
sql
SELECT element_at(map(1, 'a', 2, 'b'), 2);
>> b
flatten(arrayOfArrays):将一组嵌套数组转换为单个数组,扁平化数组。
sql
SELECT flatten(array(array(1, 2), array(3, 4)));
>> [1,2,3,4]
map_concat(map,...):返回所有map的并集
sql
SELECT map_concat(map(1, 'a', 2, 'b'), map(3, 'c'));
>> {1:"a",2:"b",3:"c"}
map_from_arrays(keys, values):使用一对给定的array创建map。给定的keys中的array的所有元素不能为NULL
sql
SELECT map_from_arrays(array('A', 'B'), array('1', '2'));
>> {"A":"1","B":"2"}
SELECT map_from_arrays(array('A', 'B', null), array('1', '2', '3'));
>> java.lang.RuntimeException: Cannot use null as map key!
map_from_entries(arrayOfEntries):使用给定的array<struct<...>>来构造map
sql
SELECT map_from_entries(array(struct(1, 'a'), struct(2, 'b')));
>> {1:"a",2:"b"}
schema_of_json(json[, options]) :将JSON STRING的内容按照DDL的格式返回
sql
SELECT schema_of_json('[{"col":0}]');
>> ARRAY<STRUCT<col: BIGINT>>
shuffle(array):对数组进行随机排列并返回
sql
SELECT shuffle(array(1, 20, null, 3));
>> [null,1,3,20]
slice(array, start, length):切割数组。将数组array从start索引开始(数组索引从1开始,如果开始为负,则从末尾开始),取指定长度的数组并返回。
sql
SELECT slice(array(1, 2, 3, 4), 2, 2);
>> [2,3]
SELECT slice(array(1, 2, 3, 4), -2, 2);
>> [3,4]
-- 超过长度的也只会取到末尾
SELECT slice(array(1, 2, 3, 4), -1, 2);
>> [4]
weekday(date):返回date/timestamp的星期(0=星期一,1=星期二,...,6=星期日)
sql
SELECT weekday('2025-12-31');
>> 2
zip_with(left, right, func):使用Lambda函数将两个给定的数组按元素合并为一个数组。如果一个数组较短,则在应用函数之前,在末尾附加null以匹配较长数组的长度。
sql
SELECT zip_with(array(1, 2, 3), array('a', 'b', 'c'), (x, y) -> (y, x));
>> [{"y":"a","x":1},{"y":"b","x":2},{"y":"c","x":3}]
SELECT zip_with(array(1, 2), array(3, 4), (x, y) -> x + y);
>> [4,6]
SELECT zip_with(array('a', 'b', 'c'), array('d', 'e', 'f'), (x, y) -> concat(x, y));
>> ["ad","be","cf"]
与arrays_zip的区别:
arrays_zip仅能合并,返回类型是array<struct>
zip_with可自定义Lambda函数(如相加、拼接等操作),返回类型是array,内部的类型看执行的函数,不一定。
其他复杂函数详解:
explode(array/map):将array或者map炸开成多行。
sql
-- 注意:array炸开的字段名默认是col
select explode(array(1, 2, null));
>>
"col"
"1"
"2"
"NULL"
-- 注意:map炸开的字段名默认是key和value
select explode(map('a',1,'b',2));
>>
"key" "value"
"a" "1"
"b" "2"
-- 重命名
select explode(array(1, 2, null)) as test_col;
>>
"test_col"
"1"
"2"
"NULL"
select explode(map('a',1,'b',2)) as (k1,v1);
>>
"k1" "v1"
"a" "1"
"b" "2"
inline(array<struct<...>>) :专门炸开array<struct<...>>的UDF,与explode相比,不仅能炸成多行,还能将struct里面的字段变成多列。
sql
-- 注意:炸开的默认字段名是col1,col2...
SELECT inline(array(struct(1, 'a'), struct(2, 'b')));
>>
"col1" "col2"
"1" "a"
"2" "b"
-- 注意:与explode的区别
select explode(array(struct(1, 'a'), struct(2, 'b')));
>>
"col"
"{""col1"":1,""col2"":""a""}"
"{""col1"":2,""col2"":""b""}"
LATERAL VIEW:用于配合表生成函数(如explode、inline)将一行包含复杂数据类型(如array、map)的数据拆分成多行数据的关键语法,从而实现数据的"行转列"或扁平化处理。具体是把"炸开后的多行"数据和原表的数据关联在一起,相当于对原表的每一行执行一次explode,然后横向拼接。
Lateral View 的基本语法结构为:LATERAL VIEW [OUTER] udtf(expression) virtual_table_alias AS column_alias1, column_alias2...
》》 LATERAL VIEW explode
sql
select *
from (
select array(1,3,5) as arr, 'N1' as name
union all
select array(2,4,6) as arr, 'N2' as name
) as tmp
LATERAL VIEW explode(arr) t as v
where v < 5;
>> 可以看到v < 5的数据被过滤掉了
"arr" "name" "v"
"[1,3,5]" "N1" "1"
"[1,3,5]" "N1" "3"
"[2,4,6]" "N2" "2"
"[2,4,6]" "N2" "4"
》》 LATERAL VIEW inline
sql
select *
from (
select array(struct(1, 'a'), struct(2, 'b')) as arr, 'N1' as name
union all
select array(struct(3, 'c'), struct(4, 'd')) as arr, 'N2' as name
) as tmp
LATERAL VIEW inline(arr) t as test1,test2
;
>>
"arr" "name" "test1" "test2"
"[{""col1"":1,""col2"":""a""},{""col1"":2,""col2"":""b""}]" "N1" "1" "a"
"[{""col1"":1,""col2"":""a""},{""col1"":2,""col2"":""b""}]" "N1" "2" "b"
"[{""col1"":3,""col2"":""c""},{""col1"":4,""col2"":""d""}]" "N2" "3" "c"
"[{""col1"":3,""col2"":""c""},{""col1"":4,""col2"":""d""}]" "N2" "4" "d"