如何在PostgreSQL中透视表并获取列和行的总数?

时间:2016-01-18 15:36:56

标签: postgresql pivot-table crosstab totals

创建临时表

CREATE TEMPORARY TABLE temperature(
id SERIAL,
date timestamp NOT NULL,
temperature integer NOT NULL
);

插入数据

-- insert this data twice (48 rows)
INSERT INTO temperature(date, temperature) VALUES('2010-01-01','0');
INSERT INTO temperature(date, temperature) VALUES('2010-02-01','1');
INSERT INTO temperature(date, temperature) VALUES('2010-03-01','2');
INSERT INTO temperature(date, temperature) VALUES('2010-04-01','3');
INSERT INTO temperature(date, temperature) VALUES('2010-05-01','4');
INSERT INTO temperature(date, temperature) VALUES('2010-06-01','5');
INSERT INTO temperature(date, temperature) VALUES('2010-07-01','6');
INSERT INTO temperature(date, temperature) VALUES('2010-08-01','7');
INSERT INTO temperature(date, temperature) VALUES('2010-09-01','8');
INSERT INTO temperature(date, temperature) VALUES('2010-10-01','9');
INSERT INTO temperature(date, temperature) VALUES('2010-11-01','10');
INSERT INTO temperature(date, temperature) VALUES('2010-12-01','11');
INSERT INTO temperature(date, temperature) VALUES('2011-01-01','0');
INSERT INTO temperature(date, temperature) VALUES('2011-02-01','1');
INSERT INTO temperature(date, temperature) VALUES('2011-03-01','2');
INSERT INTO temperature(date, temperature) VALUES('2011-04-01','3');
INSERT INTO temperature(date, temperature) VALUES('2011-05-01','4');
INSERT INTO temperature(date, temperature) VALUES('2011-06-01','5');
INSERT INTO temperature(date, temperature) VALUES('2011-07-01','6');
INSERT INTO temperature(date, temperature) VALUES('2011-08-01','7');
INSERT INTO temperature(date, temperature) VALUES('2011-09-01','8');
INSERT INTO temperature(date, temperature) VALUES('2011-10-01','9');
INSERT INTO temperature(date, temperature) VALUES('2011-11-01','10');
INSERT INTO temperature(date, temperature) VALUES('2011-12-01','11');

想要最终结果:

 year | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec | total
------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-------
 2010 |   0 |   2 |   4 |   6 |   8 |  10 |  12 |  14 |  16 |  18 |  20 |  22 |   132
 2011 |   0 |   2 |   4 |   6 |   8 |  10 |  12 |  14 |  16 |  18 |  20 |  22 |   132
------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-------
total |   0 |   4 |   8 |  12 |  16 |  20 |  24 |  28 |  32 |  36 |  40 |  44 |   264

到目前为止我做了什么:

SELECT to_char(date, 'yyyy-mm') AS date,
       SUM(temperature) AS total_temp
FROM temperature
WHERE date >= '2010-01-01' AND date<= '2012-12-31'
GROUP BY to_char(date, 'yyyy-mm')
ORDER BY to_char(date, 'yyyy-mm') ASC;

将结果返回为:

 id |        date         | temperature
----+---------------------+-------------
  1 | 2010-01-01 00:00:00 |           0
  2 | 2010-02-01 00:00:00 |           1
  3 | 2010-03-01 00:00:00 |           2
  4 | 2010-04-01 00:00:00 |           3
  5 | 2010-05-01 00:00:00 |           4
  6 | 2010-06-01 00:00:00 |           5
  7 | 2010-07-01 00:00:00 |           6
  8 | 2010-08-01 00:00:00 |           7
  9 | 2010-09-01 00:00:00 |           8
 10 | 2010-10-01 00:00:00 |           9
 11 | 2010-11-01 00:00:00 |          10
 12 | 2010-12-01 00:00:00 |          11
 13 | 2011-01-01 00:00:00 |           0
 14 | 2011-02-01 00:00:00 |           1
 15 | 2011-03-01 00:00:00 |           2
 16 | 2011-04-01 00:00:00 |           3
 17 | 2011-05-01 00:00:00 |           4
 18 | 2011-06-01 00:00:00 |           5
 19 | 2011-07-01 00:00:00 |           6
 20 | 2011-08-01 00:00:00 |           7
 21 | 2011-09-01 00:00:00 |           8
 22 | 2011-10-01 00:00:00 |           9
 23 | 2011-11-01 00:00:00 |          10
 24 | 2011-12-01 00:00:00 |          11
:

问题:

  1. 如何获得每列和每行的总和?
  2. 如何进行控制:如果&#34;总和&#34;每列(264)的总和等于每行的总和(264)然后写264(请查看上面想要的最终结果),否则写&#34; R&#34;。
  3. 修改

    根据@Gordon Linoff的回答,我提出了查询:

    SELECT to_char(date, 'yyyy') AS year, 
    SUM(CASE WHEN to_char(date, 'mm') = '01' THEN temperature ELSE 0 END) AS JAN,
    SUM(CASE WHEN to_char(date, 'mm') = '02' THEN temperature ELSE 0 END) AS FEB,
    SUM(CASE WHEN to_char(date, 'mm') = '03' THEN temperature ELSE 0 END) AS MAR,
    SUM(CASE WHEN to_char(date, 'mm') = '04' THEN temperature ELSE 0 END) AS APR,
    SUM(CASE WHEN to_char(date, 'mm') = '05' THEN temperature ELSE 0 END) AS MAY,
    SUM(CASE WHEN to_char(date, 'mm') = '06' THEN temperature ELSE 0 END) AS JUN,
    SUM(CASE WHEN to_char(date, 'mm') = '07' THEN temperature ELSE 0 END) AS JUL,
    SUM(CASE WHEN to_char(date, 'mm') = '08' THEN temperature ELSE 0 END) AS AUG,
    SUM(CASE WHEN to_char(date, 'mm') = '09' THEN temperature ELSE 0 END) AS SEP,
    SUM(CASE WHEN to_char(date, 'mm') = '10' THEN temperature ELSE 0 END) AS OCT,
    SUM(CASE WHEN to_char(date, 'mm') = '11' THEN temperature ELSE 0 END) AS NOV,
    SUM(CASE WHEN to_char(date, 'mm') = '12' THEN temperature ELSE 0 END) AS DEC,
    SUM(temperature) AS Total
    FROM   temperature
    WHERE date>= '2010-01-01' AND date<= '2012-12-31' 
    GROUP  BY 1;
    

    返回结果:

     year | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec | total
    ------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-------
     2010 |   0 |   2 |   4 |   6 |   8 |  10 |  12 |  14 |  16 |  18 |  20 |  22 |   132
     2011 |   0 |   2 |   4 |   6 |   8 |  10 |  12 |  14 |  16 |  18 |  20 |  22 |   132
    

1 个答案:

答案 0 :(得分:1)

这实际上是几件事的组合:

  • 正确的group by条款。
  • 条件聚合
  • Rollup(可在当前版本中获得,9.5)

SQL查询如下所示:

SELECT to_char(date, 'yyyy') AS month, 
       SUM(CASE WHEN to_char(date, 'mm') = '01' THEN something ELSE 0 END) AS JAN,
       SUM(CASE WHEN to_char(date, 'mm') = '02' THEN something ELSE 0 END) AS FEB,
       . . .
       SUM(CASE WHEN to_char(date, 'mm') = '12' THEN something ELSE 0 END) AS DEC
FROM kmi_d 
WHERE date >= '2010-01-01' AND date <= '2012-12-31' 
GROUP BY ROLLUP( to_char(date, 'yyyy') )
ORDER BY to_char(date, 'yyyy') ASC