在MySQL和oracle中有列转行函数用于将列转成行。在大数据SQL中也有类似的操作。这里主要讲解Spark的列转行操作。
欢迎关注微信公众号:大数据报文
在介绍列转行之前首先要说一下concat
函数,为后面列转行做一下铺垫:
-- mysql:可以一到合并多个值,无法使用分隔符,如果合并的值中有null,则合并的结果为null
select concat('11','22',null);
--mysql:可以合并多个值,第一个参数是分隔符。如果有null则忽略null合并其他值
select concat_ws(',','11','22',null);
--oracle:只能连接两个字符串,如果有一个是null,则结果是非null的值
select concat('111',null) from dual;
Spark的concat函数:
--spark sql:与mysql的规则相同
orclDF.select(concat($"CERT_NO", lit(null))).show()
-- mysql:有null值忽略,加distinct参数可以在列转行之前去重
select name,group_concat(age) from student group by name;
select name,group_concat(DISTINCT age) from student group by name;--去重
select id,group_concat(name separator ';') from aa group by id;--以逗号分割
select id,group_concat(name order by name desc) from aa group by id;--按合并字段排序
-- Oracle:合并多列默认以逗号分隔
select ci_id,wm_concat(stu_name) nameslist from student group by ci_id;
--将两列合并为一列再列转行
select ci_id,wm_concat(stu_name || '-' || stu_age) nameslist from student group by ci_id;
//spark sql:要稍微复杂一点,要使用多个函数配合
//首先分组以后,肯定是要用聚合函数的,sparksql列转行的算子叫concat_ws,第一个参数是分隔符,如果不需要刻意写空字符串,但是必须指定。第二个参数分组之后dept列的列表。如果是collect_set,表示集合,即后者表示对组内的值去重
orclDF.select($"id", $"dept", $"dest").groupBy($"id").agg(concat_ws("&", collect_list("dept"))
//通过上面这种方式可以达到上面RDBMS SQL的效果,但是如果合并两列就有些麻烦了,他没有合适的连接符
//下面这种方式,先合并了col1,再合并col2,如果要(col1[1]+col2[1])(col1[2]+col2[2])去合并需要使用concat函数
orclDF.select($"id", $"dept", $"dest").groupBy($"id").agg(concat_ws("&", collect_list("col1"),collect_list("col2"))
-- 下面这种方式是先将两列(应该说三列)合并为一列再列转行
orclDF.select($"id", $"date", concat($"dept", lit("|"), $"dest").as("track"))
.groupBy($"id")
.agg(concat_ws(",", collect_list("track")).as("tracks"))
//上面也可以这样写
val testDF = orclDF.select($"id", $"name", $"score1", $"score2")
.groupBy($"id")
.agg(concat_ws(";", collect_list(concat($"score1",lit(","),$"score2"))).as("scores"))
Spark SQL的列转行稍微有些复杂,但是逻辑很清晰,本质上还是与传统列转行一样,只是通过这种半编程方式实现起来相对而言稍显复杂。
另外最近复习Spark的一点感触。Spark SQL虽然不是传统sql,但是已经十分接近传统SQL的功能了,绝大多数传统SQL,Spark SQL都有实现。而且DataSet拥有类RDD编程的便利,可以说某些方面Spark SQL要比传统SQL灵活很多。