SQL四种方法实现行列转换超详细

Posted 楚生辉

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL四种方法实现行列转换超详细相关的知识,希望对你有一定的参考价值。

前言

大家好,我是楚生辉,在未来的日子里我们一起来学习大数据SQL相关的技术,一起努力奋斗,遇见更好的自己!

本文详细的介绍了多个方法实现列转行,行转列,并提供了案例的材料,有需要的小伙伴可以自行获取与学习~

  • 数据准备
 CREATE TABLE `score` (
   `id` varchar(255),
   `subject` char(10),
   `score` int
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 insert  into `score`(`id`,`subject`,`score`) values ('1','MATH',90),('1','ENGLISH',98),('1','CHINESE',85),('2','MATH',87),('2','ENGLISH',78),('2','CHINESE',89);

ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

1.使用join拼接

 SELECT id,score as 'MATH' FROM score WHERE subject = 'MATH';

我们把其他几门科目的成绩查出来后当做临时表再使用join不就解决了该问题吗?!而连接条件便是std。看到这,大家可以自己试一试。完整代码如下:

SELECT * FROM
	( SELECT id, score AS 'MATH' FROM score WHERE subject = 'MATH' ) AS t1
	JOIN ( SELECT id, score FROM score WHERE subject = 'ENGLISH' ) AS t2 ON t1.id = t2.id
	JOIN ( SELECT id, score FROM score WHERE subject = 'CHINESE' ) AS t3 ON t1.id = t3.id

ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
然后我们只需要对上述的结果,挑选出我们想要的数据即可

SELECT t1.id, t1.MATH, t2.score AS 'ENGLISH',t3.score AS 'CHINESE' FROM
	( SELECT id, score AS 'MATH' FROM score WHERE subject = 'MATH' ) AS t1
	JOIN ( SELECT id, score FROM score WHERE subject = 'ENGLISH' ) AS t2 ON t1.id = t2.id
	JOIN ( SELECT id, score FROM score WHERE subject = 'CHINESE' ) AS t3 ON t1.id = t3.id

ㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

2.自然拼接

自动的寻找2表中的(所有)同名且属性相同的列作为连接条件。使用natural join子句来完成。
例如:A表中有列a,b,c,d B表中有a,b,x,z
自然连接会将A.a=B.a and A.b=B.b 作为连接条件
select * from A natural join B (natural 不可以省略)。他们所得的结果中,同名且属性相同的字段只显示一个。

对于自然连接而言,连接两个table之后,两个table共用的属性就会合并在一起。如果连个table没有共有的属性,则进行笛卡尔乘积,也就是进行两两相乘,如果table 1有3行,table 2有4行,自然连接后就有12行。自然连接的语法如下:

SELECT * FROM
	( SELECT id, score AS 'MATH' FROM score WHERE subject = 'MATH' ) AS t1
	NATURAL JOIN ( SELECT id, score AS 'ENGLISH' FROM score WHERE SUBJECT = 'ENGLISH' ) AS t2
	NATURAL JOIN ( SELECT id, score AS 'CHINESE' FROM score WHERE SUBJECT = 'CHINESE' ) AS t3

ㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

3.使用union拼接

union:会将两个结果集进行并集处理,不包括重复的行;
union all:对两个结果集进行并集处理,包括重复行。

日常开发中,能使用union all就使用union all

SELECT id,score AS 'MATH',0 AS 'ENGLISH',0 AS 'CHINESE' FROM score WHERE subject = 'MATH';

ㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

(SELECT id,score AS 'MATH',0 AS 'ENGLISH',0 AS 'CHINESE' FROM score WHERE subject = 'MATH')
UNION ALL
(SELECT id,0 AS 'MATH',score AS 'ENGLISH',0 AS 'CHINESE' FROM score WHERE subject = 'ENGLISH')
UNION ALL
(SELECT id,0 AS 'MATH',0 AS 'ENGLISH',score AS 'CHINESE' FROM score WHERE subject = 'CHINESE');

ㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

此时,我们发现目前的sql查询出来会有很多重复的行,但由于其他科目没有的数据都是0,我们可以根据id进行分组,然后sum()聚合相加一下,这样就能得到我们想要的结果

select id,SUM(MATH) AS 'MATH',SUM(ENGLISH) AS 'ENGLISH',SUM(CHINESE)AS CHINESE from (
(SELECT id,score AS 'MATH',0 AS 'ENGLISH',0 AS 'CHINESE' FROM score WHERE subject = 'MATH')
UNION ALL
(SELECT id,0 AS 'MATH',score AS 'ENGLISH',0 AS 'CHINESE' FROM score WHERE subject = 'ENGLISH')
UNION ALL
(SELECT id,0 AS 'MATH',0 AS 'ENGLISH',score AS 'CHINESE' FROM score WHERE subject = 'CHINESE')) t
GROUP BY id

以上都是列转行,反过来思路也大致一样就可以实现从行转列

SELECT id, 'MATH' subject, MATH score FROM products WHERE MATH IS NOT NULL
UNION
SELECT id, 'ENGLISH' subject, ENGLISH score FROM products WHERE ENGLISH IS NOT NULL
UNION
SELECT id, 'CHINESE' subject, CHINESE score FROM products WHERE CHINESE IS NOT NULL;

4.经典sum+if

思路:由多行变为一行,自然而然的就要想要对id进行groupby聚合,在此基础上,我们还需要根据课程名词去筛选课程成绩,因此还需要再添加一个if函数作为筛选(用case when)也可以,如果if符合条件,就设置本课程的分数,如果不符合条件,就设置为null,最后我们再通过一个sum聚合函数提取成绩即可

SELECT id,  
      if(subject='MATH', score, NULL) as `MATH`,  
      if(subject='ENGLISH', score, NULL) as `ENGLISH`, 
      if(subject='CHINESE', score, NULL) as `CHINESE`
FROM score 

ㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

该步骤与上面union中自己设置0有异曲同工之妙,只不过这一次是通过if判断自动的设置为null,我们只需要在此基础上,对id进行分组,再添加一个sum聚合一下就可以实现我们的需求

SELECT id,  
      sum(if(subject='MATH', score, NULL)) as `MATH`,  
      sum(if(subject='ENGLISH', score, NULL)) as `ENGLISH`, 
      sum(if(subject='CHINESE', score, NULL)) as `CHINESE`
FROM score 
GROUP BY id 

ㅤㅤㅤㅤ ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

报表如何实现行列互换效果?

通常我们设计的二维的交叉报表,横向的维度和纵向的维度是固定的,而用户希望更希望能根据自己的需要快速转换横向纵向维度来查看报表。

技术图片

如上图所示,我们通过点击一个按钮或文字,就可将报表的行列维度互相转换,这样的动态转换效果要怎么设置实现呢?

这里我介绍两种方法实现方法:

1. 两张报表超链接

2. 单张报表动态判断

两张报表超链接

实现思路:

二维的交叉报表行列维度有两种情况,我们只需对应两种情况做两张报表,切换的按钮用超链接实现,连接的目标就是另一张报表。

具体实现:

1. 新建一张报表,文件名为行列转换 1.rpx, 数据源使用润乾设计器下自带的 demo 数据源,数据集使用 SQL 检索方式,SQL 为:SELECT 订单. 货主地区,  订单. 订单金额, 雇员. 姓氏 + 雇员. 名字 as 雇员  FROM 订单, 雇员 WHERE    订单. 雇员 ID = 雇员. 雇员 ID 。

技术图片

2. 设置报表单元格表达式,润乾设计器中单元格可以直接写表达式,也支持拖拽字段生成表达式。

B1 单元格我们通过拖拽字段的方式生成表达式:在设计器右下角的数据集面板中可以看到我们刚才新建的数据集和字段,选择对应字段,然后选择分组,将字段拖拽到 B1 单元格中,

B1 单元格就会出现 =ds1.group(货主地区; 货主地区);

技术图片

A2 单元格同 B1 的操作,不同的是选择雇员字段拖拽,生成的表达式为 =ds1.group(雇员; 雇员:1);

B2 单元格选择订单金额,选择汇总,汇总方式为求和,拖拽字段到 B2 单元格中,表达式为 =ds1.sum(订单金额)。

技术图片

3. 修改单元格扩展方式,单元格的扩展方向默认为纵向,选中 B1,在右侧属性面板中设置扩展方式为横向扩展。

技术图片

4.A1 单元格中直接写上行列互换,设置超链接表达式:“./showReport.jsp?rpx=/30 交互报表 / 行列互换 2.rpx”,超链接窗口为:_self。

技术图片

5. 将当前报表另存一张,保存文件名为“行列转换 2.rpx”,将 B1 和 A2 单元格表达式对调,将 A1 的超链接表达式修改为:“./showReport.jsp?rpx=/30 交互报表 / 行列互换 1.rpx”。

经过以上设置,我们就完成了最不烧脑的二维报表行列互换报表的制作,使用 showReport.jsp 访问行列转换 1.rpx,点击行列转换那几个字就会变成行列转换 2.rpx,用户尽管点击切换,不用做条件判断。

单张报表动态判断

实现思路:

根据标志 flag 当前值,来改变他的值,根据这个值确定横向分组和纵向分组使用的字段。

具体实现

1. 将行列转换 1.rpx 另存一个报表,文件名为行列转换.rpx。

2. 报表中增加一个普通参数,参数名称为 change, 数据类型为整数,默认的参数值为 1;

两个动态参数:参数名称 group1, 数据类型为字符串,值表达式为 if(change==1,“货主地区”,“雇员”;

参数名称 Group2, 数据类型为字符串,值表达式为 if(change==1,“雇员”,“货主地区”)。

技术图片

3. 修改单元格表达式

横向分组和纵向分组的字段通过动态参数的值,动态变化,将 B1 的表达式修改为 =ds1.group(${group1};${group1}:1),将 A2 的表达式修改为 =ds1.group(${group2};${group2}:1)。

技术图片

4. 修改行列互换的超链接表达式

超链接表达式为:“./showReport.jsp?rpx=/30 交互报表 / 行列互换.rpx&change=”+if(change==1,-1,1)

技术图片

经过以上设置,我们通过 showReport.jsp 访问行列转换.rpx, 在浏览器地址栏中,输入 url:

http://localhost:6868/demo/reportJsp/showReport.jsp?rpx=/30 交互报表 / 行列转换.rpx访问。

技术图片

当点击表头中的行列转换文字,刷新的报表还是当前的报表,但 change 的参数值已经变成了 -1,并且行列的分组字段也发生了变化。

技术图片

通过上面介绍的两种方法都能快速的实现报表行列互换的效果,如果不想做多张报表来实现,推荐使用第二种实现方法,如果不习惯条件判断的设置,可以使用第一种方法,虽然涉及到两张报表,只是另存第一张报表,简单修改就行,修改的工作量可忽律不计,也没有第二种方法的小弯弯绕,具体根据自己的情况选择合适的方法吧。

以上是关于SQL四种方法实现行列转换超详细的主要内容,如果未能解决你的问题,请参考以下文章

SQL行列转换6种方法

oracle中一个表的行列转换

sql server动态行列转换

SQL Server pivot行列转换案例分析

SQL行列转换

Pandas行列转换的4大技巧