Mysql查询根据两列动态将行转换为列

Posted

技术标签:

【中文标题】Mysql查询根据两列动态将行转换为列【英文标题】:Mysql query to dynamically convert rows to columns on the basis of two columns 【发布时间】:2013-07-31 07:23:12 【问题描述】:

我在这里遵循question 使用mysql 查询将行动态转换为列。这很好用,但我需要根据两列进行转换,

上面链接中提到的查询适用于单列“数据”,但我想为“数据”和“价格”两列工作。

我在这里添加了一个示例,

给定一个表格 A,它看起来像

Table A

|  id|order|data|item|Price|
-----+-----+----------------
|   1|    1|   P| 1  | 50  |
|   1|    1|   P| 2  | 60  |
|   1|    1|   P| 3  | 70  |
|   1|    2|   Q| 1  | 50  |
|   1|    2|   Q| 2  | 60  |
|   1|    2|   Q| 3  | 70  |
|   2|    1|   P| 1  | 50  |
|   2|    1|   P| 2  | 60  |
|   2|    1|   P| 4  | 80  |
|   2|    3|   S| 1  | 50  |
|   2|    3|   S| 2  | 60  |
|   2|    3|   S| 4  | 80  |

我喜欢编写如下所示的查询:

Result Table

|  id|order1|order2|order3|item1|item2|item3|item4|
-----+-----+---------------------------------------
|   1|    P |    Q |      | 50  | 60  | 70  |     |
|   2|    P |      |    S | 50  | 60  |     | 80  |

我尝试创建两个不同的查询,然后创建一个连接来实现这一点,但这可能不是一个好的解决方案。任何人都可以提出与上面链接中提到的相同的解决方案吗?

谢谢

【问题讨论】:

【参考方案1】:

如果您有已知数量的 orderitem 值,那么您可以将查询硬编码为:

select id,
  max(case when `order` = 1 then data end) order1,
  max(case when `order` = 2 then data end) order2,
  max(case when `order` = 3 then data end) order3,
  max(case when item = 1 then price end) item1,
  max(case when item = 2 then price end) item2,
  max(case when item = 3 then price end) item3,
  max(case when item = 4 then price end) item4
from tableA
group by id;

见Demo。但是您将遇到的部分问题是因为您正在尝试转换多列数据。我获得最终结果的建议是首先取消数据透视。 MySQL 没有 unpivot 函数,但您可以使用 UNION ALL 将多对列转换为行。 unpivot 的代码将类似于以下内容:

select id, concat('order', `order`) col,  data value
from tableA
union all
select id, concat('item', item) col, price value
from tableA;

见Demo。结果将是:

| ID |    COL | VALUE |
-----------------------
|  1 | order1 |     P |
|  1 | order1 |     P |
|  1 | order1 |     P |
|  1 |  item1 |    50 |
|  1 |  item2 |    60 |
|  1 |  item3 |    70 |

如您所见,这已将order/dataitem/price 的多列转换为多行。完成后,您可以使用带有 CASE 的聚合函数将值转换回列:

select id, 
  max(case when col = 'order1' then value end) order1,
  max(case when col = 'order2' then value end) order2,
  max(case when col = 'order3' then value end) order3,
  max(case when col = 'item1' then value end) item1,
  max(case when col = 'item2' then value end) item2,
  max(case when col = 'item3' then value end) item3
from
(
  select id, concat('order', `order`) col,  data value
  from tableA
  union all
  select id, concat('item', item) col, price value
  from tableA
) d
group by id;

见Demo。最后,需要将上面的代码转换成动态的prepared statement查询:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when col = ''',
      col,
      ''' then value end) as `', 
      col, '`')
  ) INTO @sql
FROM
(
  select concat('order', `order`) col
  from tableA
  union all
  select concat('item', `item`) col
  from tableA
)d;

SET @sql = CONCAT('SELECT id, ', @sql, ' 
                  from
                  (
                    select id, concat(''order'', `order`) col,  data value
                    from tableA
                    union all
                    select id, concat(''item'', item) col, price value
                    from tableA
                  ) d
                  group by id');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

见SQL Fiddle with demo。这给出了一个结果:

| ID | ORDER1 | ORDER2 | ORDER3 | ITEM1 | ITEM2 |  ITEM3 |  ITEM4 |
-------------------------------------------------------------------
|  1 |      P |      Q | (null) |    50 |    60 |     70 | (null) |
|  2 |      P | (null) |      S |    50 |    60 | (null) |     80 |

【讨论】:

如果我有 2 个表:列表和信息表会发生什么,当我尝试修改查询时,只显示一行而不是所有信息

以上是关于Mysql查询根据两列动态将行转换为列的主要内容,如果未能解决你的问题,请参考以下文章

MySQL - 根据每列的最新日期和 id 列表将行转换为列

将行值转换为列,并计算所有可能值 mysql 的重复次数

如何使用 Postgresql 将行转换为列?

使用 PIVOT 将行转换为列

不能从子查询将行转换为列

根据条件将行转换为列