MaxCompute列转行、行转列详解

原文标题:ODPS SQL ——列转行、行转列这回让我玩明白了!

原文作者:阿里云开发者

冷月清谈:

- **什么是列转行、行转列?** - 列转行:将一行数据中某一列或几列展开,形成多行数据。 - 行转列:将多行数据中某一列拼接成一列或几列,形成一行数据。
  • MaxCompute中如何实现列转行、行转列?

    • 列转行:TRANS_ARRAY函数或LATERAL VIEW EXPLODE函数。
    • 行转列:WM_CONCAT函数。
  • 应用场景

    • 数据清洗:将不规范的数据结构转换成易于分析的结构。
    • 数据转换:实现不同数据结构之间的转换。
  • 注意

    • TRANS_ARRAY和LATERAL VIEW EXPLODE在处理空值时有细微差别。
    • 列转行和行转列是互逆操作。
  • 具体案例

    • 将自选标签列表(self_code_list)转成标签列表(a_tag_list)。



怜星夜思:


1、TRANS_ARRAY和LATERAL VIEW EXPLODE函数有什么区别?
2、列转行、行转列还有哪些更高级的应用场景?
3、在实际项目中,你们是如何使用列转行、行转列技术的?有哪些技巧可以分享?

原文内容

阿里妹导读


本文详细介绍了在MaxCompute中如何使用TRANS_ARRAY和LATERAL VIEW EXPLODE函数来实现列转行的功能。

使用场景

有这样一种场景,需要将下面A表中的self_code_list转化为a_tag_list,self_code到a_tag有一一映射关系的,这个映射关系在B表中。对于映射关系的转化一般是用join的方式去解决(目前没有想到更好的方式,如果有哪位大神有更好的方式欢迎在评论区留言)。

图1-A表

图2-B表

对目前这种数据结构肯定是不好处理的,但是如果转化成图3中所示:

图3

就可以用直接用self_code关联B表从而得到a_tag的值,如图4中所示,思路清晰、操作简单粗暴。





图4

两种列转行的姿势

所以核心的问题来了,应该怎么操作把A表转成图3中的样子,这种操作其实就是列转行,解释一下,就是把一行数据的某列(一般是数组)或者几列展开,并选某列或者某几列作为展开的key, 把一行数据转成多行数据。在前面把表A从图1转成图3的案例中,我们是以id, name做为key把self_code_list这一列展开成多行。

在odps的内建函数中有两个函数可以帮我们轻而易举地完成列转行:

TRANS_ARRAY
https://www.alibabacloud.com/help/zh/maxcompute/user-guide/trans-array

LATERAL VIEW  EXPLODE(column)

https://www.alibabacloud.com/help/zh/maxcompute/user-guide/lateral-view

使用TRANS_ARRAY


SELECT  TRANS_ARRAY(2,',',id,name,self_code_list) AS (id,name,self_code)
 FROM  (
           SELECT  id,name
                  ,ARRAY_JOIN(FROM_JSON(JSON_FORMAT(self_code_list),"array<string>"),',')
                   AS self_code_list
             FROM    TABLE_A
             ORDER BY id ASC
 )

表A里self_code_list字段类型是JSON,而TRANS_ARRAY 则要求转为行的的列类型必须是String,所以先把self_code_list转化为String 类型。

这里结合这个列子解释一下这个函数的参数:

trans_array (<num_keys>, <separator>, <key1>,<key2>,…,<col1>,<col2>,<col3>) as (<key1>,<key2>,...,<col1>, <col2>)

第一个参数是列转行时做为key的列数,在本例中我们用id和name作为key,所以是2。

第二个参数是把一个String展开为多个String,也就是一行变多行的分割符,根据具体数据的分割符号而定,一般是逗号,分号等。

剩下的参数是String类型的列名,函数会根据第一个参数来判断最后M个列是要展开的列,前面N个列是作为key的列。在本例子中我们列名参数依次是id, name, slef_code_list, 而num_key = 2, 所以结果集中id, name 两列会作为key 列,而self_code_list则是被展开的列。


使用LATERAL VIEW EXPLODE

SELECT  id
       ,name
       ,self_code
FROM    TABLE_A
       LATERAL VIEW EXPLODE(FROM_JSON(JSON_FORMAT(self_code_list),"array<string>")) tmp AS self_code;

需要注意的是EXPLODE 函数的入参必须是ARRAY的。

两种方式都是可以实现列转行,但是两者在处理为空的列会有细微的差别。

看下这几条原始的数据:

SELECT id, name, self_code_list from TABLE_A

where id IN (291, 112, 116, 252);

图5

针对这四条数据分别用两种方式做转化。

使用TRANS_ARRAY

图6

使用LATERAW VIEW EXPLODE

图7

可以看到使用LATERAW VIEW EXPLODE的方式结果集不会保留为空的行,而TRANS_ARRAY的方式则会保留为空的行。

列转行是行转列的逆操作

好了,列转行聊完了,该说说行转列。还记得我们初衷吗 ?我们是要把TableA的self_code_list映射成a_tag_list, 如图8所示。经过前面的列转行操作就可以很轻易的和TABLE表关联,得到图4所示的临时表。

图8

从图4到图8的操作就是行转列,也就是把多行数据转化成一列或多个列。当然了这也不是瞎转的,跟列转行一样在转化时需要根据key来转化。列转行行转列是一个互逆的过程,在列转行时我们把每行的某列值拆分为多个值,然后按照key变成多行。那么行转列就是根据key把多行数据的某列拼接成一份数据,再依据key变成一行。在本例图4-图8的过程中,我们以id, name做为key, 对atag列用逗号做拼接,id, name, a_tag_list组成唯一的一行。当然也可以转成多列,只需要在拼接的时候指定列的区分方式,然后再对列值做SPLIT 操作即可得到多列。这种拼接的方式可以通过函数WM_CONCAT。
https://www.alibabacloud.com/help/zh/maxcompute/user-guide/wm-concat

在上面的例子中我们是这样使用WM_CONCAT函数的:

SELECT  id
       ,name
       ,WM_CONCAT(',',a_tag) a_tag
from
T_tmp_4;

这样我们就得到了图8所示的结果集。

至此我们完成了表的列转行、行转列,并最终达成了我们的目标。关于更多表的行转列、列转行还可以参考MaxCompute官方文档:

https://www.alibabacloud.com/help/zh/maxcompute/use-cases/transpose-rows-to-columns-or-columns-to-rows


注意key的选择,尽量选择唯一性高的列。

TRANS_ARRAY效率更高,但LATERAL VIEW EXPLODE语法更简洁。

数据聚合:将相同key对应的数据合并,进行聚合计算。

缺失值填充:通过转置数据,将缺失值填充完整。

使用临时表避免重复计算。

先分析数据结构,确定转行还是转列更合适。

善用MaxCompute生态工具,如DataWorks、DMS等。

TRANS_ARRAY保留空值行,而LATERAL VIEW EXPLODE不保留。

结合UDF提高性能。

join优化:通过转置数据,将join条件转为过滤条件,提高join效率。

TRANS_ARRAY可用于JSON数据,LATERAL VIEW EXPLODE只能用于数组数据。

TRANS_ARRAY需要手动指定key数量,LATERAL VIEW EXPLODE自动识别key。

数据透视:将行列互换,方便分析不同维度的数据。