MySQL - 将来自同一个表的多个选择组合成一个带有group by的结果表

时间:2013-04-21 20:56:20

标签: mysql sql database pivot

我有一个仪表读数表,其中包含一个米,没有读数值和读数。

我想显示每年每年读数的总和。

例如:

SELECT meterNo, SUM(readingValue) '2009' FROM readings
WHERE readingDate >= '2009-01-01' AND readingDate <= '2009-12-31'
GROUP BY meterNo;

将显示表格:

   meterNo  2009
   --------------
     4       50
     5       5
     12      30
     13      63
     18      18
     26      484
     27      21
     28      510
     29      28

这就是我想要的,它显示了2009年每个仪表的总读数值。但是,我需要结果表来显示每年的一列。所以它会是这样的:

   meterNo  2009  2010  2011  2012
   --------------------------------
     1             20    35     50
     2                   45     35
     3                   50     60
     4       50    35    20     30
     5       5     10    10     15
     6                          30

等等......

我可以重用查询的where部分:

WHERE readingDate >= '2009-01-01' AND readingDate <= '2009-12-31'

只需更改每年的日期,但如何在一个结果表中显示每个WHERE结果作为自己的列?

3 个答案:

答案 0 :(得分:7)

MySQL没有PIVOT函数,但您可以使用带有CASE表达式的聚合函数将数据行转换为列。

如果您的years数量有限,则可以对查询进行硬编码:

select meterNo,
  sum(case when year(readingDate) = 2009 then readingValue else 0 end) `2009`,
  sum(case when year(readingDate) = 2010 then readingValue else 0 end) `2010`,
  sum(case when year(readingDate) = 2011 then readingValue else 0 end) `2011`,
  sum(case when year(readingDate) = 2012 then readingValue else 0 end) `2012`,
  sum(case when year(readingDate) = 2013 then readingValue else 0 end) `2013`
from readings
group by meterno;

请参阅SQL Fiddle with Demo

但是,如果要将未知数量的值或要作为新年度调整的查询添加到数据库中,那么可以使用预准备语句生成动态SQL:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'sum(CASE WHEN year(readingDate) = ',
      year(readingDate),
      ' THEN readingValue else 0 END) AS `',
      year(readingDate), '`'
    )
  ) INTO @sql
FROM readings;

SET @sql 
  = CONCAT('SELECT meterno, ', @sql, ' 
            from readings
            group by meterno');

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

SQL Fiddle with Demo。两者都给出了结果:

| METERNO | 2009 | 2010 | 2012 | 2013 | 2011 |
----------------------------------------------
|       1 |   90 |  180 |    0 |   90 |   90 |
|       2 |   50 |    0 |   90 |    0 |    0 |
|       3 |   80 |   40 |   90 |   90 |    0 |

作为旁注,如果您希望null在没有值而不是零的行中显示,则可以删除else 0(请参阅Demo

答案 1 :(得分:2)

您可以使用CASE WHEN执行此操作。它允许您根据多个条件对多个值求和。在你的例子中,你可以做这样的事情(我简化了我的例子,因为我无法访问你的实际数据):

SELECT meterNo, 
sum(CASE WHEN readingDate = 2009 THEN readingValue END) as '2009',
sum(CASE WHEN readingDate = 2010 THEN readingValue END) as '2010',
sum(CASE WHEN readingDate = 2011 THEN readingValue END) as '2011',
sum(CASE WHEN readingDate = 2012 THEN readingValue END) as '2012'
FROM readings
GROUP BY meterNo

您需要使用自己的条件切换条件 - sum(CASE WHEN readingDate >= '2009-01-01' AND readingDate <= '2009-12-31' THEN readingValue END)等 - 但它应该有效。这是我在sqlFiddle上做的一个工作示例:http://sqlfiddle.com/#!2/6990e/29。输出为五列,每个米的总和没有。

答案 2 :(得分:0)

你可以这样做:

SELECT 
    meterNo, SUM(readingValue) AS `total`, YEAR(readingDate)
FROM 
    readings
GROUP BY 
    meterNo, YEAR(readingDate);

结果:

METERNO     TOTAL   YEAR(READINGDATE)
1           90      2009
1           90      2010
2           50      2009
3           80      2009
3           40      2010

另请注意如何使用YEAR函数而不是where子句。

SEE FIDDLE