MySQL中的面试题——行转列(列转行)手把手详解思想
作者:mmseoamin日期:2023-12-13

目录

1、什么是行转列,列转行 ?

1.1、概念:

1.2、行转列和列转行的思想

1.2.1、行转列的思想?

1.2.2、列转行的思想?

3、实操讲解

3.1、实现行转列 

3.2、总结:行转列实现的两种方法   

3.3、实现列转行 

3.4、总结 :列转行

4、动手练习


1、什么是行转列,列转行 ?

1.1、概念:

        在MySQL中,行转列(Pivot)和列转行(Unpivot)是用于改变数据表格布局的概念,行转列和列转行是在特定数据转换需求下使用的技术,可以帮助改变数据的呈现方式,以适应不同的分析和报告要求。

        行转列:是指将原始数据表格中的行数据转换为列数据。这种操作常用于将某一列的值作为新的列名,并将对应的值填入相应的位置。

        列转行:列转行是指将原始数据表格中的列数据转换为行数据。这种操作常用于将多个列的值转换为一列,并将原来的列名作为新的列。

  场景: 

MySQL中的行转列和列转行:
    此时我们可以看出,同一个人他的每一科成绩信息都作为单独的一行来进行展示,现
    在我们想要将他的成绩信息展示在同一行上,这个时候,就是所谓的列转行;
    反之,就是我们的行转列。
          张三          语文          78
          张三          数学          87
          张三          英语          65
          李四          语文          88
          李四          数学          90
  	      李四          英语          98
          王五          语文          73
  	      王五          数学          88
  	      王五          英语          90
    其目的就是将我们的数据展示为:
          name      语文          数学           英语
          张三       78            87            65
          李四       88            90            98
          王五       73            88            90

1.2、行转列和列转行的思想

1.2.1、行转列的思想?

        把它想做为一个拆分的过程,你想一想,一行数据想要作为多行数据进行展示,不把它拆开来,那怎么行!而MySQL中具有拆分的功能有哪些:首先肯定得从基础语法中想一下,我们所学习的MySQL中就有一个分组的思想(group by),函数方面还有一个case....when等,当什么什么满足时,进行匹配。 

  太枯燥了上图:

create table test_group_by(
    name varchar(30) NOT NULL COMMENT '学生姓名',
    chinese DOUBLE COMMENT '语文成绩',
    math DOUBLE COMMENT '数学成绩',
    english DOUBLE COMMENT '英语成绩'
);
insert into test_group_by values('张三',80,null,null),
                                ('张三',null,90,null),
                                ('张三',null,null,100);

此时此刻,使用聚合函数会是什么样的结果:

select name,sum(chinese) as '语文',sum(math) as '数学',sum(english) as '英语' from test_group_by group by name;

这就是我们想要的结果,那在实际的操作中,没有null这一列,该怎么搞?想象!

MySQL中的面试题——行转列(列转行)手把手详解思想,第1张

1.2.2、列转行的思想?

        反观,当有一个很分散的数据,想有一个合并的操作,这个时候首先就会想到MySQL中的聚合函数sum()、union、union all操作。

也是直接上图:

create table test(
     name varchar(30) comment '测试-姓名',
     chinese double comment '测试-语文成绩',
     math double comment '测试-数学成绩',
     english double comment '测试-英语成绩'
);
insert into test value ('张三',10,80,90);
update test set chinese = 70 where name = '张三';
select * from test;

查看数据:

MySQL中的面试题——行转列(列转行)手把手详解思想,第2张如何转换呢?思想:现在我们要将这行数据列转行,所以必须要查这张表,那么这张表数据给的   有了,但是没有我们想要的列,所以得我们自个儿生成!

-- 现在我们想要学科的列和成绩的列,from从表中查询到数据,此时直接将写入字段值,实际就是在传递此时查询到的值
select name,
    '语文' as 'subject' ,chinese as 'score',
    '数学' as 'subject' ,math as 'score',
    '英语' as 'subject' ,english as 'score'
from test;

展示结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第3张

好家伙也不是我们想要的,他把我们想要得三个并排了,所以我们现在有没有一个办法,先查询一个,最后再把他们整合起来,有!就是使用union all方法!注意:使用union all得使用需要保证字段一致

-- 最后再把他们整合起来,有!就是使用union all方法!,注意:使用union all得使用需要保证字段一致
select name,
       '语文' as 'subject',chinese as 'score'
from test
union all
select name,
       '数学' as 'subject',math as 'score'
from test
union all
select name,
       '英语' as 'subject',english as 'score'
from test;

3、实操讲解

3.1、实现行转列 

/*-------------------------- MySQL中的行转列和列转行 ---------------------------------
-- MySQL中的行转列
-- 对于我们的stu_score这张表,我们的数据展示是以行为主,同一个人物的信息被拆分为多个行,行转列的思想就是想把行数据转换为列数据
  MySQL中的行转列和列转行
          张三          语文          78
          张三          数学          87
          张三          英语          65
          李四          语文          88
          李四          数学          90
  	      李四          英语          98
          王五          语文          73
  	      王五          数学          88
  	      王五          英语          90
其目的就是将我们的数据展示为:
          name      语文          数学           英语
          张三       78            87            65
          李四       88            90            98
          王五       73            88            90
  假想:要想实现上边行转列之后的表,我们先观察其表结构,其包含四个字段,分别是:姓名字段、语文成绩字段、数学成绩字段、英语成绩字段
  伪列,前边介绍行转列和列转行的思想中已经提到,行转列要用到分组聚合的思想,现在我们观察原表中的字段,每行都包含了
       姓名字段、各学科字段、各学科成绩字段,那现在我们把表中的每一行都追加上其他学科的成绩字段。最终我们根据名字字段来group by,
       是不是就解决了问题,那这个列怎么来,想象呗!
          张三          语文          78          null (数学)           null (英语)
          张三          数学          87          null (语文)           null (英语)
          张三          英语          65          null (数学)           null (语文)
          李四          语文          88
          李四          数学          90
  	      李四          英语          98
          王五          语文          73
  	      王五          数学          88
  	      王五          英语          90
  行转列的解题步骤:
    1、确定分组列、转换列、数据列---确定数据中哪一个作为分组列(找同名字段,上边的也就是根据name进行分组),
                               哪一个作为转换列(上表也就是将各科成绩转换为列),哪一个作为数据列
    2、生成伪列---声明伪列,即保证结构的完整性
    3、做分组查询---为第四步做铺垫,分组查询通常跟我们的聚合函数一起使用
    4、选择合适的聚合函数---使用聚合函数,把结构中的数据正确的计算出来(上边用的就是sum求和函数)
 */

1)第一步:生成分组列(name)、确定转换列(subject)、确定数据列(score)

2)生成伪列

-- 生成伪列——扫描全表。根据case when end的语法,我们通过匹配查找,来筛选我们的学科,也就是当匹配中出现了语文项,就返回语文学科的成绩
select name,
       case subject when '语文' then score else null end as 'chinese',
       case subject when '数学' then score else null end as 'math',
       case subject when '英语' then score else null end as 'english'
from stu_score;
-- 也可以使用if,因为判断的条件比较简单
select name,
       IF(subject = '语文', score, null) as 'chinese',
       if(subject = '数学', score, null) as 'math',
       if(subject = '英语', score, null) as 'english'
from stu_score;

 结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第4张

3)确定分组列

4)确定聚合函数

-- 第三步、第四步:前边由于生成了笛卡尔积,所以我们需要使用分组聚合,在这里就选择name作为分组列,sum来作为聚合函数,因为sum函数在进行分组求和的时候忽略NULL值
-- 第一种方法
select name,
       sum(case subject when '语文' then score else null end) as 'chinese',
       sum(case subject when '数学' then score else null end) as 'math',
       sum(case subject when '英语' then score else null end) as 'english'
from stu_score group by name;
-- 第二种方法:通过子查询把查询到的结果作为临时表
select name,
       sum(语文) as '语文',
       sum(数学) as '数学',
       sum(英语) as '英语'
from(
    select name,
    case subject when '语文' then score else null end as '语文',
    case subject when '数学' then score else null end as '数学',
    case subject when '英语' then score else null end as '英语'
    from stu_score
    ) temp
group by name;
-- 第三种方法:使用max聚合函数
select name,
    max(case subject when '语文' then score else null end) as '语文',
    max(case subject when '数学' then score else null end) as '数学',
    max(case subject when '英语' then score else null end) as '英语'
from stu_score group by name;
-- 将上面的行转列的查询结果存储为学生信息的表
create table stu_score_row_columns as (
    select name,
    max(case subject when '语文' then score else null end) as '语文',
    max(case subject when '数学' then score else null end) as '数学',
    max(case subject when '英语' then score else null end) as '英语'
from stu_score group by name
);

 结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第5张

3.2、总结:行转列实现的两种方法   

两步法:
公式:
select 分组列,
      聚合函数(m1)as 列名1,
      聚合函数(m2)as 列名2,
      聚合函数(m3)as 列名3,
from (select *,
    case 转换列 when 转换列值1 then 数据列 else .... end as m1,    
    case 转换列 when 转换列值2 then 数据列 else .... end as m2,
    case 转换列 when 转换列值3 then 数据列 else .... end as m3
    from 表名)临时表名
group by 分组列;
一步法:
公式:
select 分组列,
    聚合函数(case 转换列 when 转换列值1 then 数据列 else .... end) as 列名1,    
    聚合函数(case 转换列 when 转换列值2 then 数据列 else .... end) as 列名2,
    聚合函数(case 转换列 when 转换列值3 then 数据列 else .... end) as 列名3
    ...
from 表名
group by 分组列;

3.3、实现列转行 

  创建数据表:

-- 将上面的行转列的查询结果存储为学生信息的表
create table stu_score_row_columns as (
    select name,
    max(case subject when '语文' then score else null end) as '语文',
    max(case subject when '数学' then score else null end) as '数学',
    max(case subject when '英语' then score else null end) as '英语'
from stu_score group by name
);
/*
数据展示:
    name 语文 数学 英语
    张三  78  87  65
    李四  88  90  98
    王五  73  88  90
列转行之后:思想:先转换我们第一行的数据,然后一层一层拼接使用union all来操作
    张三          语文          78
    张三          数学          87
    张三          英语          65
分析:
    1、表中的张三是通过表中的name所得到,语文作为我们的表头,可以通过'列名'获取,列所对应的值通过列名直接指定
 */

   结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第6张

   实现列转行,根据前面介绍的思想,一个道理,也是自己生成新的列,from查出来的值,可以直接书写,及代表引用该值

 -- 先获取一行的数据,其中,subject列的值始终为字符串'语文',score列的值将与stu_score_row_columns表中的语文列的值相对应。
select name,'语文' as subject,语文 as 'score' from stu_score_row_columns
union all
select name,'数学' as subject,数学 as 'score' from stu_score_row_columns
union all
select name,'英语' as subject,英语 as 'score' from stu_score_row_columns
order by name;

  结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第7张

3.4、总结 :列转行

总结:实现列转行方法

解题步骤:

    1、确定转换列,非转换列

    2、生成新列

    3、使用union或union all来进行合并

    4、根据需要进行order by排序操作

公式:

select 非转换列,'转换列1' as 新转换列名,转换列1 as 新数据列名 from 表名

union all

select 非转换列,'转换列2' as 新转换列名,转换列2 as 新数据列名 from 表名

union all

select 非转换列,'转换列3' as 新转换列名,转换列3 as 新数据列名 from 表名  

order by ....;s

注意:

        新转换列名和新数据列名必须保持一致。

4、动手练习

行转列(Pivot)题目:

1.  给定一个订单表格,包含订单号(order_number)、产品名称(product_name)和销售数量(quantity)三个字段,将该表格行转列,以订单号作为列名,各产品名称对应的销售数量作为相应的值。

创建表,并插入数据:

 create table order_tab(
     order_number int comment '订单号',
     product_name varchar(40) comment '产品名称',
     quantity int comment '销售数量'
 ) COMMENT '订单表';
insert into order_tab(order_number, product_name, quantity) values (1,'球鞋',3),
                    (1,'羽毛球',3),
                    (3,'羽毛球',2),
                    (2,'球鞋',6),
                    (4,'矿泉水',3),
                    (2,'苏打水',1),
                    (2,'矿泉水',4);
-- 确定我们的行转列
select order_number '订单编号',
       sum(case when product_name = '球鞋' then quantity else 0 end) as '球鞋',
       sum(case when product_name = '羽毛球' then quantity else 0 end) as '羽毛球',
       sum(case when product_name = '矿泉水' then quantity else 0 end) as '矿泉水',
       sum(case when product_name = '苏打水' then quantity else 0 end) as '苏打水'
from order_tab group by order_number order by order_number;

  结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第8张

列转行(Unpivot)题目:

1. 假设有一个销售数据表格,包含年份(year)、产品A的销售量(product_a_sales)和产品B的销售量(product_b_sales)两个字段,将该表格列转行,以年份为一列,并列出每个产品和对应的销售量。

2. 给定一个市场调查数据表格,包含城市名称(city_name)、产品A的需求量(product_a_demand)和产品B的需求量(product_b_demand)两个字段,将该表格列转行,以城市名称为一列,并列出每个产品和对应的需求量。

  解题2:

  数据准备:

-- 实现列转行(数据准备)
create table market_servey(
    city_name varchar(30) comment '城市名称',
    product_a_demand int comment '产品A的需求量',
    product_b_demand int comment '产品B的需求量'
);
insert into market_servey(city_name, product_a_demand, product_b_demand) values ('贵阳',20,30),
                                 ('毕节',30,10),
                                 ('遵义',15,50),
                                 ('铜仁',60,10),
                                 ('黔东南',10,16);

 MySQL中的面试题——行转列(列转行)手把手详解思想,第9张

  代码:

-- 实现列转行(列转行使用到union all)
select city_name,'product_a_demand' as '产品名称',product_a_demand as '产品数量' from market_servey
union all
select city_name,'product_b_demand' as '产品名称',product_b_demand as '产品数量' from market_servey
order by city_name;

  结果:

MySQL中的面试题——行转列(列转行)手把手详解思想,第10张