百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT技术 > 正文

SQL实用技巧-行列转换

wptr33 2024-12-03 18:47 18 浏览

PIVOT关键字对于指定的每一组行值,都会生成对应的列。PIVOT关键字是FROM子句的一部分,可以和JOIN等其他关键字一同使用

在编写大数据SQL的时候,有时需要进行行列的转化。

什么是行列转化?如下图,不同商品在不同月份的销量数据,有时候我们希望数据和左侧一样的排列,但原始数据却像右侧一样排列,此时我们需要把右侧的列排列转换成左侧的行排列,反之亦然。

下面以上面这个例子为大家介绍一些行列转换的方式。

一、行转列

使用CASE WHEN

适用场景:MySQL、Hive、Spark SQL。

把行转换成列最简单的方式就是使用CASE WHEN。

case month when '2024-01' then sales end的意思是当month的值为'2024-01'时取sales的值,其他情况取NULL,因此可以计算出不同月份的销量。

select  product
        ,max(case month when '2024-01' then sales end) as month_01
        ,max(case month when '2024-02' then sales end) as month_02
        ,max(case month when '2024-03' then sales end) as month_03
from    sales_row
group by product

使用PIVOT

适用场景:Spark SQL。

PIVOT关键字对于指定的每一组行值,都会生成对应的列。PIVOT关键字是FROM子句的一部分,可以和JOIN等其他关键字一同使用。

SELECT ... 
FROM ... 
PIVOT ( 
    <aggregate function> [AS <alias>] [, <aggregate function> [AS <alias>]] ... 
    FOR (<column> [, <column>] ...) 
    IN ( 
        (<value> [, <value>] ...) AS <new column> 
        [, (<value> [, <value>] ...) AS <new column>] 
        ... 
       ) 
    ) 
[...]

参数

是否必选

说明

aggregate function

聚合函数

alias

聚合函数的别名,别名和最终PIVOT处理过后生成的列名相关

column

指定转换为列的行值在源表中的列名称

value

指定转换为列的行值

new column

转换后新的列名称

直接看示例。

利用PIVOT把month列按值聚合出了三列month_01,month_02,month_03。

select  *
from    sales_row 
PIVOT (
     MAX(sales) for month in(
       '2024-01' as month_01, 
       '2024-02' as month_02, 
       '2024-03' as month_03
     )
)

二、列转行

使用UNION ALL

适用场景:MySQL、Hive、Spark SQL。

UNION ALL相当于取每一个列的值,然后并联在一起,注意'2024-01' as month中的2024-01是字符串。

使用UNION ALL的好处就是,无论是mysql、hive还是spark都支持,以不变应万变。

缺点就是当要关联列比较多时比较麻烦,如果要查询全年的数据,则需要UNION ALL 12次,如果是天数据则要UNION ALL 365次。

select  *
from    (
    select product, '2024-01' as month, month_01 from sales_column
    union all
    select product, '2024-02' as month, month_02 from sales_column
    union all
    select product, '2024-03' as month, month_03 from sales_column
)

仅使用EXPLODE

适用场景:Spark SQL。

explode可以将一个数组或者map分解成多行,例如:

select explode(split('A,B,C', ','))

# 结果
col
A
B
C
select explode(map('2024-01', 1000, '2024-02', 2000, '2024-03', 3000))

# 结果
key     value
2024-01 1000
2024-02 2000
2024-03 3000

对于列转行的需求,可以先创建一个map之后再利用explode拆分成多行。

注意下面SQL中,explode函数返回值有两个,因此设置列别名时需要用as (month, sales)。

select  product
        ,explode(
          map('2024-01', month_01, 
              '2024-02', month_02, 
              '2024-03', month_03)
        ) as (month, sales)
from    sales_column

类似的思路还可以利用concat+trans_array等操作。

hive中的UDTF

上面的方式仅适用于Spark。

当使用UDTF函数(explode就是一个UDTF函数)的时候,Hive只允许对拆分字段进行访问。

select explode(map('2024-01', 1000, '2024-02', 2000, '2024-03', 3000))

# 结果
key     value
2024-01 1000
2024-02 2000
2024-03 3000

也就是说在Hive中,上面SQL是没问题的,下面的SQL就会报错了

hive> select  product
    >   ,explode(map('2024-01', month_01, '2024-02', month_02, '2024-03', month_03))
  >  from    sales_column

SemanticException [Error 10081]: UDTF's are not supported outside the SELECT clause, nor nested in expressions

因此这块需要使用LATERAL VIEW功能来进行处理。LATERAL VIEW将explode生成的结果当做一个视图来处理。

使用Lateral View

适用场景:Hive、Spark SQL。

lateral view为侧视图,意义是为了配合UDTF来使用,把某一行数据拆分成多行数据。

Hive中不加lateral view的UDTF只能提取单个字段拆分。加上lateral view就可以将拆分的单个字段数据与原始表数据关联上。

LATERAL VIEW [ OUTER ] generator_function ( expression [ , ... ] ) [ table_alias ] AS column_alias [ , ... ]

参数

是否必选

说明

generator_function

将一行数据拆成多行数据的UDTF (EXPLODE, INLINE等)

table_alias

UDTF结果的别名

columnAlias

拆分后得到的列的别名

直接看如何利用lateral view实现列转行。

select  product, t_view.month, t_view.sales
from    sales_column
lateral view explode(
    map('2024-01', month_01, '2024-02', month_02, '2024-03', month_03)
) t_view as month, sales

其中explode(map('2024-01', month_01, '2024-02', month_02, '2024-03', month_03))把map分解成多行。

lateral view同时指定了这个侧视的表名t_view和两列的列名month 、sales。

lateral view explode(
    map('2024-01', month_01, '2024-02', month_02, '2024-03', month_03)
) t_view as month, sales

# 模拟结果,lateral view不能单独使用
month sales
2024-01 1000
2024-02 1100
2024-03 1200
2024-01 1100
2024-02 1000
2024-03 1400

此时select product, t_view.month, t_view.sales就能达成UDTF拆分的单个字段数据与原始表数据关联的效果了。

select  product, t_view.month, t_view.sales
from    sales_column

# 结果
product month sales
A 2024-01 1000
A 2024-02 1100
A 2024-03 1200
B 2024-01 1100
B 2024-02 1000
B 2024-03 1400

使用UNPIVOT

适用场景:Spark 3.4+。

UNPIVOT关键字对于指定的每一组列,都会生成对应的行。其中UNPIVOT关键字是FROM子句的一部分,可以和JOIN关键字等其他关键字一同使用。

SELECT ...
FROM ...
UNPIVOT (
  <new column of value> [, <new column of value>] ...
  FOR (<new column of name> [, <new column of name>] ...)
  IN (
      (<column> [, <column>] ...) [AS (<column value> [, <column value>] ...)]
      [, (<column> [, <column>] ...) [AS (<column value> [, <column value>] ...)]]
      ...
    )
)
[...]

参数说明如下:

参数

是否必选

说明

new column of value

转换后新生成的列名称,该列的值由指定转换为行的列的值填充。

new column of name

转换后新生成的列名称,该列的值由指定转换为行的列名称填充。

column

指定转换为行的列名称,列的名称用来填充new column of name;列的值用来填充new column of value。

column value

指定转换为行的列的别名

也是直接看示例:

select  *
from    sales_column 
UNPIVOT (
  sales for month in (month_01 as '2024-01', month_02 as '2024-02', month_03 as '2024-03')
)

sales for month in (month_01, month_02, month_03)的意思就是生成一个新列sales,这一列的值是month_01, month_02, month_03这三列的值。

生成一个新列month, 这里一列的值是month_01, month_02, month_03这三列的列名,即'2024-01', '2024-02', '2024-03'。


相关推荐

每天一个编程技巧!掌握这7个神技,代码效率飙升200%

“同事6点下班,你却为改BUG加班到凌晨?不是你不努力,而是没掌握‘偷懒’的艺术!本文揭秘谷歌工程师私藏的7个编程神技,每天1分钟,让你的代码从‘能用’变‘逆天’。文末附《Python高效代码模板》,...

Git重置到某个历史节点(Sourcetree工具)

前言Sourcetree回滚提交和重置当前分支到此次提交的区别?回滚提交是指将改动的代码提交到本地仓库,但未推送到远端仓库的时候。...

git工作区、暂存区、本地仓库、远程仓库的区别和联系

很多程序员天天写代码,提交代码,拉取代码,对git操作非常熟练,但是对git的原理并不甚了解,借助豆包AI,写个文章总结一下。Git的四个核心区域(工作区、暂存区、本地仓库、远程仓库)是版本控制的核...

解锁人生新剧本的密钥:学会让往事退场

开篇:敦煌莫高窟的千年启示在莫高窟321窟的《降魔变》壁画前,讲解员指着斑驳色彩说:"画师刻意保留了历代修补痕迹,因为真正的传承不是定格,而是流动。"就像我们的人生剧本,精彩章节永远...

Reset local repository branch to be just like remote repository HEAD

技术背景在使用Git进行版本控制时,有时会遇到本地分支与远程分支不一致的情况。可能是因为误操作、多人协作时远程分支被更新等原因。这时就需要将本地分支重置为与远程分支的...

Git恢复至之前版本(git恢复到pull之前的版本)

让程序回到提交前的样子:两种解决方法:回退(reset)、反做(revert)方法一:gitreset...

如何将文件重置或回退到特定版本(怎么让文件回到初始状态)

技术背景在使用Git进行版本控制时,经常会遇到需要将文件回退到特定版本的情况。可能是因为当前版本出现了错误,或者想要恢复到之前某个稳定的版本。Git提供了多种方式来实现这一需求。...

git如何正确回滚代码(git命令回滚代码)

方法一,删除远程分支再提交①首先两步保证当前工作区是干净的,并且和远程分支代码一致$gitcocurrentBranch$gitpullorigincurrentBranch$gi...

[git]撤销的相关命令:reset、revert、checkout

基本概念如果不清晰上面的四个概念,请查看廖老师的git教程这里我多说几句:最开始我使用git的时候,我并不明白我为什么写完代码要用git的一些列指令把我的修改存起来。后来用多了,也就明白了为什么。gi...

利用shell脚本将Mysql错误日志保存到数据库中

说明:利用shell脚本将MYSQL的错误日志提取并保存到数据库中步骤:1)创建数据库,创建表CreatedatabaseMysqlCenter;UseMysqlCenter;CREATET...

MySQL 9.3 引入增强的JavaScript支持

MySQL,这一广泛采用的开源关系型数据库管理系统(RDBMS),发布了其9.x系列的第三个更新版本——9.3版,带来了多项新功能。...

python 连接 mysql 数据库(python连接MySQL数据库案例)

用PyMySQL包来连接Python和MySQL。在使用前需要先通过pip来安装PyMySQL包:在windows系统中打开cmd,输入pipinstallPyMySQL ...

mysql导入导出命令(mysql 导入命令)

mysql导入导出命令mysqldump命令的输入是在bin目录下.1.导出整个数据库  mysqldump-u用户名-p数据库名>导出的文件名  mysqldump-uw...

MySQL-SQL介绍(mysql sqlyog)

介绍结构化查询语言是高级的非过程化编程语言,允许用户在高层数据结构上工作。它不要求用户指定对数据的存放方法,也不需要用户了解具体的数据存放方式,所以具有完全不同底层结构的不同数据库系统,可以使用相同...

MySQL 误删除数据恢复全攻略:基于 Binlog 的实战指南

在MySQL的世界里,二进制日志(Binlog)就是我们的"时光机"。它默默记录着数据库的每一个重要变更,就像一位忠实的史官,为我们在数据灾难中提供最后的救命稻草。本文将带您深入掌握如...