最近碰到一个问题就是,现在有每一行的两列是a和b的两个数据,比如可能是a的身高和b的身高在同一行,并且其他相关数据比如体重,性别都在同一行,字段名字都一样只不过前面有加前缀a_和b_,类似以下这种情况:
select * from temp.explore_test;
这里只有前缀的不同,现在我想要的是,a和b拆开各成一行,每一行代表一个人的数据,怎么做?
最终的结果应该是:
id | card_no | new_balance |
---|---|---|
14053 | 44528 | 10000 |
14092 | 11992 | 7000 |
14789 | 77789 | 5000 |
14526 | 89333 | 6000 |
其实可以从最简单的原理思考入手,我们这里肯定是要用到一个函数实现的,就叫explode,顾名思义就是将行扩展为列,有点像链反应式的爆炸。我们看一下官方文档。
其实explode可以对两种结构进行操作,一个是列表array,另一个是字典,但我们这里操作的是array。
我们思考一下实现的逻辑,可以发现其实就是要把a的数据放一行,b的数据放一行,分开,所以a的数据和b的数据要在一次explode当中完成,所以首先要把a的数据连在一起,b的数据连在一起,然后explode之后a和b数据就分开了,之后再把a和b的数据分成相应的列就可以了。
我们看一下具体实现的代码。
第一步,a和b的数据放一块。我们要用concat_ws
函数将a的数据连接起来,b的数据也连接起来。
我们测试一下结果:
select *, concat_ws(',', cast(a_id as string), cast(a_card_no as string), cast(a_new_balance as string) ) as a_result from temp.explode_test;
可以看到,a的结果确实已经按照分隔符‘,’连接起来了。在这里要注意的是,需要先把数据转换成字符串才能够连接。
我们创建一个临时表
DROP TABLE IF EXISTS temp.un_fold;create table temp.un_foldasselect *, concat_ws( '/', a_result, b_result) as final_resultfrom(select *,concat_ws(',', cast(a_id as string), cast(a_card_no as string), cast(a_new_balance as string) ) as a_result,concat_ws(',', cast(B_id as string), cast(B_card_no as string), cast(B_new_balance as string) ) as b_resultfrom temp.explode_test) a;
结果
第二步,以上面的ab数据的分隔符’/’拆分a和b的数据。
create table temp.get_ab_results asSELECT * from temp.un_foldLATERAL VIEW explode(split(final_result, '/')) dummy as result_unfold;select * from temp.get_ab_results;
现在就2行扩展为4行了,也就是每一行的不同人的数据已经分开了。接下来就是把a的列分开。
create table temp.get_column_results asselect *, split(result_unfold, ',')[0] as id,split(result_unfold, ',')[1] as card_no,split(result_unfold, ',')[2] as new_balancefrom temp.get_ab_resultsselect * from temp.get_column_results
在这里我们用split
方法将之前用,
连接起来的列数据重新分开,并赋列名。
我们可以看到最终能够得到我们要的结果。
总结:
一开始我们需要把a和b的数据分开,所以先要把a的数据和b的数据分别捆绑在一起,然后用explode分开以后再分开a和b的列。