MySQL - 行到列

标签 mysql sql pivot-table etl crosstab

我尝试搜索帖子,但只找到了 SQL Server/Access 的解决方案。我需要 MySQL (5.X) 中的解决方案。

我有一个表(称为历史记录),包含 3 列:hostid、itemname、itemvalue。
如果我进行选择(从历史记录中选择*),它将返回

   +--------+----------+-----------+
   | hostid | itemname | itemvalue |
   +--------+----------+-----------+
   |   1    |    A     |    10     |
   +--------+----------+-----------+
   |   1    |    B     |     3     |
   +--------+----------+-----------+
   |   2    |    A     |     9     |
   +--------+----------+-----------+
   |   2    |    C     |    40     |
   +--------+----------+-----------+

如何查询数据库以返回类似内容

   +--------+------+-----+-----+
   | hostid |   A  |  B  |  C  |
   +--------+------+-----+-----+
   |   1    |  10  |  3  |  0  |
   +--------+------+-----+-----+
   |   2    |   9  |  0  |  40 |
   +--------+------+-----+-----+

最佳答案

我将添加一个更长、更详细的说明来说明解决此问题的步骤。如果太长,我深表歉意。

<小时/>

我将从您给出的基础开始,并用它来定义几个术语,我将在本文的其余部分使用这些术语。这将是基表:

select * from history;

+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
|      1 | A        |        10 |
|      1 | B        |         3 |
|      2 | A        |         9 |
|      2 | C        |        40 |
+--------+----------+-----------+

这将是我们的目标,漂亮的数据透视表:

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

history.hostid 列中的值将成为数据透视表中的y 值history.itemname 列中的值将变为 x-values(出于显而易见的原因)。

<小时/>

当我必须解决创建数据透视表的问题时,我使用三步过程来解决它(带有可选的第四步):

  1. 选择感兴趣的列,即 y 值x 值
  2. 使用额外的列扩展基表 - 每个 x 值一列
  3. 对扩展表进行分组和聚合 - 每个 y 值一组
  4. (可选)美化聚合表

让我们将这些步骤应用于您的问题,看看会得到什么:

第 1 步:选择感兴趣的列。在所需的结果中,hostid 提供y 值itemname 提供x 值

第 2 步:使用额外列扩展基表。我们通常需要每个 x 值一列。回想一下,我们的 x 值列是 itemname:

create view history_extended as (
  select
    history.*,
    case when itemname = "A" then itemvalue end as A,
    case when itemname = "B" then itemvalue end as B,
    case when itemname = "C" then itemvalue end as C
  from history
);

select * from history_extended;

+--------+----------+-----------+------+------+------+
| hostid | itemname | itemvalue | A    | B    | C    |
+--------+----------+-----------+------+------+------+
|      1 | A        |        10 |   10 | NULL | NULL |
|      1 | B        |         3 | NULL |    3 | NULL |
|      2 | A        |         9 |    9 | NULL | NULL |
|      2 | C        |        40 | NULL | NULL |   40 |
+--------+----------+-----------+------+------+------+

请注意,我们没有更改行数 - 我们只是添加了额外的列。另请注意 NULL 的模式 - itemname = "A" 的行的新列 A 具有非空值,并且其他新列的空值。

第 3 步:对扩展表进行分组和聚合。我们需要按 hostid 分组,因为它提供了 y 值:

create view history_itemvalue_pivot as (
  select
    hostid,
    sum(A) as A,
    sum(B) as B,
    sum(C) as C
  from history_extended
  group by hostid
);

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 | NULL |
|      2 |    9 | NULL |   40 |
+--------+------+------+------+

(请注意,我们现在每个 y 值一行。) 好的,我们就快到了!我们只需要摆脱那些丑陋的 NULL 即可。

第四步:美化。我们将用零替换任何空值,以便结果集看起来更好:

create view history_itemvalue_pivot_pretty as (
  select 
    hostid, 
    coalesce(A, 0) as A, 
    coalesce(B, 0) as B, 
    coalesce(C, 0) as C 
  from history_itemvalue_pivot 
);

select * from history_itemvalue_pivot_pretty;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

我们就完成了——我们已经使用 MySQL 构建了一个漂亮的数据透视表。

<小时/>

应用此过程时的注意事项:

  • 在额外列中使用什么值。我在此示例中使用了 itemvalue
  • 在额外列中使用什么“中性”值。我使用了 NULL,但也可能是 0"",具体取决于您的具体情况
  • 分组时使用什么聚合函数。我使用了 sum,但是 countmax 也经常使用(max 经常在构建单行时使用)分布在多行中的“对象”)
  • 使用多列表示 y 值。此解决方案不限于对 y 值使用单个列 - 只需将额外的列插入 group by 子句(并且不要忘记 select他们)

已知限制:

  • 此解决方案不允许数据透视表中有 n 列 - 在扩展基表时需要手动添加每个数据透视列。因此,对于 5 或​​ 10 个 x 值,此解决方案很好。 100 block ,不太好。有一些使用存储过程生成查询的解决方案,但它们很丑陋并且很难正确执行。当数据透视表需要有很多列时,我目前不知道有什么好方法来解决这个问题。

关于MySQL - 行到列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53200063/

相关文章:

php - MySQL 内部连接 ​​2 个表

mysql - 为什么我没有得到我期望的值(value)?

Mysql 查询,每个列具有不同类别的子查询

json - 使用 jq 将非唯一化的 json 输出转换为汇总表

php - 使用php删除html表中的记录

MySQL 插入转换

mysql - REGEXP "and"运算符

sql - 更高效的SQLite触发器来“汇总”多行

mysql - 在 MySQL 中合并多行时出现问题

excel - 如何按特定列对数据透视表进行排序?