Mysql 将列转换为行(数据透视表)

问题描述 投票:0回答:4

我有一张这样的桌子

id col1 col2 col3 col4
101 一月 A B B
102 二月 C A G E

然后我想创建这样的报告

描述 一月 二月
col1 A C
col2 B A
col3 0 G
第 4 栏 B E

有人可以帮忙吗?

mysql sql pivot unpivot
4个回答
65
投票

您需要做的是首先对数据进行逆透视,然后进行透视。但不幸的是 MySQL 没有这些函数,因此您需要使用

UNION ALL
查询 unpivot 和带有
CASE
的聚合函数来复制它们。

unpivot 或

UNION ALL
部分从 col1、col2 等获取数据并将其转换为多行:

select id, month, col1 value, 'col1' descrip
from yourtable
union all
select id, month, col2 value, 'col2' descrip
from yourtable
union all
select id, month, col3 value, 'col3' descrip
from yourtable
union all
select id, month, col4 value, 'col4' descrip
from yourtable

请参阅SQL Fiddle with Demo

结果:

|  ID | MONTH |  VALUE | DESCRIP |
----------------------------------
| 101 |   Jan |      A |    col1 |
| 102 |   feb |      C |    col1 |
| 101 |   Jan |      B |    col2 |
| 102 |   feb |      A |    col2 |
| 101 |   Jan | (null) |    col3 |
| 102 |   feb |      G |    col3 |
| 101 |   Jan |      B |    col4 |
| 102 |   feb |      E |    col4 |

然后将其包装在子查询中以应用聚合,并使用

CASE
将其转换为您想要的格式:

select descrip, 
  max(case when month = 'jan' then value else 0 end) jan,
  max(case when month = 'feb' then value else 0 end) feb
from
(
  select id, month, col1 value, 'col1' descrip
  from yourtable
  union all
  select id, month, col2 value, 'col2' descrip
  from yourtable
  union all
  select id, month, col3 value, 'col3' descrip
  from yourtable
  union all
  select id, month, col4 value, 'col4' descrip
  from yourtable
) src
group by descrip

参见 SQL Fiddle 演示

结果是:

| DESCRIP | JAN | FEB |
-----------------------
|    col1 |   A |   C |
|    col2 |   B |   A |
|    col3 |   0 |   G |
|    col4 |   B |   E |

5
投票

尽管这个问题非常古老,并且有人将其标记为“非常常见”,但人们似乎仍然发现它(包括我)并发现它很有帮助。我开发了一个更通用的版本来取消行的旋转,并认为这可能对某人有帮助。

SET @target_schema='schema';
SET @target_table='table';
SET @target_where='`id`=1';
SELECT
    GROUP_CONCAT(qry SEPARATOR ' UNION ALL ')
    INTO @sql
FROM (
    SELECT
        CONCAT('SELECT `id`,', QUOTE(COLUMN_NAME), ' AS `key`,`', COLUMN_NAME, '` AS `value` FROM `', @target_table, '` WHERE ', @target_where) qry
    FROM (
        SELECT `COLUMN_NAME` 
        FROM `INFORMATION_SCHEMA`.`COLUMNS` 
        WHERE `TABLE_SCHEMA`=@target_schema 
            AND `TABLE_NAME`=@target_table
    ) AS `A`
) AS `B`;
PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;

我在 MySQL 8.x 服务器上使用此查询并将其聚合为 JSON 对象,因此形成了

id, key, value
结果结构。


0
投票

扩展这个伟大的先前答案,因为我的帐户目前无法编辑或评论

虽然这种方法不像之前的 UNION ALL / CASE - 方法那么简单,但它的优点在于它可以(“动态”)用于 任意数量的原始列 [请纠正我的“任何” ].

可能导致不清楚的错误的限制是

group_concat_max_len系统变量,默认值为 1024

在这种情况下,只需尝试类似的方法

SET SESSION group_concat_max_len = 92160;

0
投票

假设表中的列数是固定的,并且结果表中的列数也是固定的,则可以将结果与辅助表组合起来,只是为了复制必要的数据次数,以便稍后可以转换列中的多行,例如:

drop table if exists table1;
create table table1 (id int, month varchar(10), col1 char(1), col2 char(1), col3 char(1), col4 char(1));
insert into table1 (id, month, col1, col2, col3, col4) values 
(101, 'Jan', 'A', 'B', NULL, 'B'), 
(102, 'Feb', 'C', 'A', 'G', 'E'),
(103, 'Mar', 'X', 'Y', 'Z', 'T'),
(104, 'Apr', '1', '2', '3',  NULL)
;
select * from table1;

您的数据是:

+------+-------+------+------+------+------+
| id   | month | col1 | col2 | col3 | col4 |
+------+-------+------+------+------+------+
|  101 | Jan   | A    | B    | NULL | B    |
|  102 | Feb   | C    | A    | G    | E    |
|  103 | Mar   | X    | Y    | Z    | T    |
|  104 | Apr   | 1    | 2    | 3    | NULL |
+------+-------+------+------+------+------+
4 rows in set (0,00 sec)

然后,创建辅助表,其中包含要转换的列数所需的记录:

create table amount (number int);
insert into amount (number) values (1), (2), (3), (4);

现在,只需合并两个表的结果即可:

select
  amount.number,  
 
  case when number = 1 then 'col1' 
       when number = 2 then 'col2'
       when number = 3 then 'col3'
       when number = 4 then 'col4'
  end as description,

  case when number = 1 then group_concat(case when month = 'Jan' then col1 else '' end SEPARATOR '') 
       when number = 2 then group_concat(case when month = 'Jan' then col2 else '' end SEPARATOR '')
       when number = 3 then group_concat(case when month = 'Jan' then col3 else '' end SEPARATOR '')
       when number = 4 then group_concat(case when month = 'Jan' then col4 else '' end SEPARATOR '')
  end as jan,

  case when number = 1 then group_concat(case when month = 'Feb' then col1 else '' end SEPARATOR '') 
       when number = 2 then group_concat(case when month = 'Feb' then col2 else '' end SEPARATOR '')
       when number = 3 then group_concat(case when month = 'Feb' then col3 else '' end SEPARATOR '')
       when number = 4 then group_concat(case when month = 'Feb' then col4 else '' end SEPARATOR '')
  end as feb,

  case when number = 1 then group_concat(case when month = 'Mar' then col1 else '' end SEPARATOR '') 
       when number = 2 then group_concat(case when month = 'Mar' then col2 else '' end SEPARATOR '')
       when number = 3 then group_concat(case when month = 'Mar' then col3 else '' end SEPARATOR '')
       when number = 4 then group_concat(case when month = 'Mar' then col4 else '' end SEPARATOR '')
  end as mar,

  case when number = 1 then group_concat(case when month = 'Apr' then col1 else '' end SEPARATOR '') 
       when number = 2 then group_concat(case when month = 'Apr' then col2 else '' end SEPARATOR '')
       when number = 3 then group_concat(case when month = 'Apr' then col3 else '' end SEPARATOR '')
       when number = 4 then group_concat(case when month = 'Apr' then col4 else '' end SEPARATOR '')
  end as apr

from table1
cross join amount
group by amount.number
;

结果是:

+--------+-------------+------+------+------+------+
| number | description | jan  | feb  | mar  | apr  |
+--------+-------------+------+------+------+------+
|      1 | col1        | A    | C    | X    | 1    |
|      2 | col2        | B    | A    | Y    | 2    |
|      3 | col3        |      | G    | Z    | 3    |
|      4 | col4        | B    | E    | T    |      |
+--------+-------------+------+------+------+------+
4 rows in set (0,00 sec)
© www.soinside.com 2019 - 2024. All rights reserved.