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

我有一个表(称为历史)3列:hostid, itemname, itemvalue。 如果我执行select (select * from history),它会返回

   +--------+----------+-----------+
   | 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 |
+--------+------+------+------+

历史中的价值观。Hostid列将变成数据透视表中的y值。历史中的价值观。Itemname列将变成x值(原因很明显)。


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

选择感兴趣的列,即y值和x值 用额外的列扩展基表——每个x值对应一列 对扩展表进行分组和聚合——每个y值对应一组 (可选)修饰聚合表

让我们把这些步骤应用到你的问题中,看看会得到什么:

步骤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。

第四步:美化。我们只是将null值替换为0,这样结果集看起来会更好:

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构建了一个漂亮的数据透视表。


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

what value to use in the extra columns. I used itemvalue in this example what "neutral" value to use in the extra columns. I used NULL, but it could also be 0 or "", depending on your exact situation what aggregate function to use when grouping. I used sum, but count and max are also often used (max is often used when building one-row "objects" that had been spread across many rows) using multiple columns for y-values. This solution isn't limited to using a single column for the y-values -- just plug the extra columns into the group by clause (and don't forget to select them)

已知的限制:

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

其他回答

你可以使用几个LEFT join。请使用此代码

SELECT t.hostid,
       COALESCE(t1.itemvalue, 0) A,
       COALESCE(t2.itemvalue, 0) B,
       COALESCE(t3.itemvalue, 0) C 
FROM history t 
LEFT JOIN history t1 
    ON t1.hostid = t.hostid 
    AND t1.itemname = 'A' 
LEFT JOIN history t2 
    ON t2.hostid = t.hostid 
    AND t2.itemname = 'B' 
LEFT JOIN history t3 
    ON t3.hostid = t.hostid 
    AND t3.itemname = 'C' 
GROUP BY t.hostid

如果你可以使用MariaDB,有一个非常非常简单的解决方案。

自MariaDB-10.02以来,添加了一个新的存储引擎CONNECT,可以帮助我们将另一个查询或表的结果转换为数据透视表,就像你想要的那样: 你可以看看这些文件。

首先安装connect存储引擎。

现在我们的表的主列是itemname,每一项的数据都位于itemvalue列中,所以我们可以使用这个查询得到结果数据透视表:

create table pivot_table
engine=connect table_type=pivot tabname=history
option_list='PivotCol=itemname,FncCol=itemvalue';

现在我们可以从数据透视表中选择我们想要的:

select * from pivot_table

详情请点击这里

我找到了一种方法,使用简单的查询使我的报告几乎动态地将行转换为列。你可以在这里在线查看和测试。

查询的列数是固定的,但值是动态的,并且基于行值。所以,我使用一个查询来构建表头,另一个查询来查看值:

SELECT distinct concat('<th>',itemname,'</th>') as column_name_table_header FROM history order by 1;

SELECT
     hostid
    ,(case when itemname = (select distinct itemname from history a order by 1 limit 0,1) then itemvalue else '' end) as col1
    ,(case when itemname = (select distinct itemname from history a order by 1 limit 1,1) then itemvalue else '' end) as col2
    ,(case when itemname = (select distinct itemname from history a order by 1 limit 2,1) then itemvalue else '' end) as col3
    ,(case when itemname = (select distinct itemname from history a order by 1 limit 3,1) then itemvalue else '' end) as col4
FROM history order by 1;

你也可以总结一下:

SELECT
     hostid
    ,sum(case when itemname = (select distinct itemname from history a order by 1 limit 0,1) then itemvalue end) as A
    ,sum(case when itemname = (select distinct itemname from history a order by 1 limit 1,1) then itemvalue end) as B
    ,sum(case when itemname = (select distinct itemname from history a order by 1 limit 2,1) then itemvalue end) as C
FROM history group by hostid order by 1;
+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 | NULL |
|      2 |    9 | NULL |   40 |
+--------+------+------+------+

RexTester测试结果:

http://rextester.com/ZSWKS28923

作为一个实际使用的例子,下面的报告以列的形式显示了船只/公共汽车出发和到达的时间,并有一个可视化的时间表。你会看到一个额外的列没有使用在最后一个col而不会混淆可视化: **票务系统的在线售票和存在

我的解决方案:

select h.hostid, sum(ifnull(h.A,0)) as A, sum(ifnull(h.B,0)) as B, sum(ifnull(h.C,0)) as  C from (
select
hostid,
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 
) h group by hostid

它在提交的案例中产生预期的结果。

SELECT 
    hostid, 
    sum( if( itemname = 'A', itemvalue, 0 ) ) AS A,  
    sum( if( itemname = 'B', itemvalue, 0 ) ) AS B, 
    sum( if( itemname = 'C', itemvalue, 0 ) ) AS C 
FROM 
    bob 
GROUP BY 
    hostid;