Spark SQL中列转行(UNPIVOT)的两种方法

行列之间的互相转换是ETL中的常见需求,在Spark SQL中,行转列有内建的PIVOT函数可用,没什么特别之处。而列转行要稍微麻烦点。本文整理了2种可行的列转行方法,供参考。

1 测试数据准备
本文的环境是Windows 10, Spark 2.4,开发语言是Python。首先构建一点初始测试数据,

from pyspark.sql import SparkSession

spark = SparkSession.builder.appName('TestAPP').enableHiveSupport().getOrCreate()

df = spark.createDataFrame([('数学','张三',88), ('语文','张三',92), ('英语','张三',77),
                            ('数学','王五',65), ('语文','王五',87), ('英语','王五',90),
                            ('数学','李雷',67), ('语文','李雷',33), ('英语','李雷',24),
                            ('数学','宫九',77), ('语文','宫九',87), ('英语','宫九',90)
                           ], ['科目','姓名','分数']).orderBy('科目')

df.show()

执行程序,可以看到数据如下,

+----+----+----+
    |科目|姓名|分数|
    +----+----+----+
    |数学|张三|  88|
    |数学|李雷|  67|
    |数学|宫九|  77|
    |数学|王五|  65|
    |英语|张三|  77|
    |英语|宫九|  90|
    |英语|李雷|  24|
    |英语|王五|  90|
    |语文|李雷|  33|
    |语文|宫九|  87|
    |语文|张三|  92|
    |语文|王五|  87|
    +----+----+----+

2 行转列
如上述,使用PIVOT函数即可实现行转列,

df.createOrReplaceTempView('scores')

sql_content = '''select * from scores 
                 pivot
                 (
                     sum(`分数`) for
                     `姓名` in ('张三','王五','李雷','宫九')
                 )          
              '''

df_pivot = spark.sql(sql_content)
df_pivot.show()

得到结果,

+----+----+----+----+----+
|科目|张三|王五|李雷|宫九|
+----+----+----+----+----+
|数学|  88|  65|  67|  77|
|英语|  77|  90|  24|  90|
|语文|  92|  87|  33|  87|
+----+----+----+----+----+

3 列转行
本文整理的两种办法是使用Spark中的stack函数和lateral view + explode函数,

3.1 stack()
stack(n, expr1, …, exprk) - 会将expr1, …, exprk 分割为n行.

df_pivot.createOrReplaceTempView('v_pivot')

sql_content = '''select `科目`,
                 stack(4, '张三', `张三`, '王五', `王五`, '李雷', `李雷`, '宫九', `宫九`) as (`姓名`, `分数` )
                 from  v_pivot             
              '''

df_unpivot1 = spark.sql(sql_content)

df_unpivot1.show()

可以看到,结果的结构和初始数据的结构相同,

+----+----+----+
|科目|姓名|分数|
+----+----+----+
|数学|张三|  88|
|数学|王五|  65|
|数学|李雷|  67|
|数学|宫九|  77|
|英语|张三|  77|
|英语|王五|  90|
|英语|李雷|  24|
|英语|宫九|  90|
|语文|张三|  92|
|语文|王五|  87|
|语文|李雷|  33|
|语文|宫九|  87|
+----+----+----+

3.2 lateral view + explode()
explode函数可以把数组分割为多行,比如,

> SELECT explode(array(10, 20));
 10
 20

lateral view使用表生成函数将每个输入行转换为0或多个输出行。最常见的用法是和explode函数一起使用。

sql_content = '''select `科目`, split(temp1, ':')[0] as `姓名`, split(temp1, ':')[1] as `分数`
                 from(               
                   select `科目`, concat(
                                     '张三:', `张三`, ',',
                                     '王五:', `王五`, ',',
                                     '李雷:', `李雷`, ',',
                                     '宫九:', `宫九`
                                     )  temp
                   from v_pivot
                 ) lateral view explode(split(temp, ',')) as temp1
              '''
df_unpivot2 = spark.sql(sql_content)

df_unpivot2.show()

结果同上,

+----+----+----+
|科目|姓名|分数|
+----+----+----+
|数学|张三|  88|
|数学|王五|  65|
|数学|李雷|  67|
|数学|宫九|  77|
|英语|张三|  77|
|英语|王五|  90|
|英语|李雷|  24|
|英语|宫九|  90|
|语文|张三|  92|
|语文|王五|  87|
|语文|李雷|  33|
|语文|宫九|  87|
+----+----+----+
抱歉,你的问题提到了MySQL列转行操作,但是引用的内容是关于SQL Server的。在MySQL,并没有内置的UNPIVOT函数,但是可以使用UNION操作实现类似的功能。下面是一个示例: ``` SELECT 姓名, '语文' AS 课程, 语文 AS 分数 FROM tb UNION ALL SELECT 姓名, '数学' AS 课程, 数学 AS 分数 FROM tb UNION ALL SELECT 姓名, '物理' AS 课程, 物理 AS 分数 FROM tb; ``` 以上查询将会将tb表的数据从列转换成行,并分为姓名、课程和分数三列。每个UNION ALL子句表示一个转换的列。 请注意,这只是一种在MySQL实现列转行的方式之一,具体的实现方式可能因数据库版本和表结构而有所不同。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [SQL知识点之列转行Unpivot函数](https://blog.csdn.net/weixin_30420045/article/details/113437799)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [T-Sql语法:行转列(pivot)和列转行unpivot)](https://blog.csdn.net/weixin_35975406/article/details/113437796)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值