按月计算并将月份作为列

时间:2013-05-16 21:53:01

标签: sql postgresql pivot case crosstab

背景

我每月都有时间序列数据,我想按月分组每个ID的值,然后将月份名称作为列而不是行。

实施例

+----+------------+-------+-------+
| id | extra_info | month | value |
+----+------------+-------+-------+
| 1  | abc        | jan   | 10    |
| 1  | abc        | feb   | 20    |
| 2  | def        | jan   | 10    |
| 2  | def        | feb   | 5     |
| 1  | abc        | jan   | 15    |
| 3  | ghi        | mar   | 15    |

期望结果

+----+------------+-----+-----+-----+
| id | extra_info | jan | feb | mar |
+----+------------+-----+-----+-----+
| 1  | abc        | 25  | 20  | 0   |
| 2  | def        | 10  | 5   | 0   |
| 3  | ghi        | 0   | 0   | 15  |

当前方法

我可以轻松按月分组,总结价值观。这让我:

-----------------------------------
| id | extra_info | month | value |
+----+------------+-------+-------+
| 1  | abc        | jan   | 25    |
| 1  | abc        | feb   | 20    |
| 2  | def        | jan   | 10    |
| 2  | def        | feb   | 5     |
| 3  | ghi        | mar   | 15    |

但我现在需要那些月作为列名。不知道从哪里开始。

其他信息

  • 就语言而言,此查询将在postgres中运行。
  • 上述几个月只是示例,显然真实数据集要大得多,涵盖数千个ID的所有12个月

非常感谢SQL大师的任何想法!

2 个答案:

答案 0 :(得分:6)

您可以使用带有CASE表达式的聚合函数将行转换为列:

select id,
  extra_info,
  sum(case when month = 'jan' then value else 0 end) jan,
  sum(case when month = 'feb' then value else 0 end) feb,
  sum(case when month = 'mar' then value else 0 end) mar,
  sum(case when month = 'apr' then value else 0 end) apr,
  sum(case when month = 'may' then value else 0 end) may,
  sum(case when month = 'jun' then value else 0 end) jun,
  sum(case when month = 'jul' then value else 0 end) jul,
  sum(case when month = 'aug' then value else 0 end) aug,
  sum(case when month = 'sep' then value else 0 end) sep,
  sum(case when month = 'oct' then value else 0 end) oct,
  sum(case when month = 'nov' then value else 0 end) nov,
  sum(case when month = 'dec' then value else 0 end) "dec"
from yt
group by id, extra_info

请参阅SQL Fiddle with Demo

答案 1 :(得分:2)

tablefunc模块

我会使用crosstab()。如果您还没有安装附加模块tablefunc

CREATE EXTENSION tablefunc

基础知识:
PostgreSQL Crosstab Query

如何处理额外的列:
Pivot on Multiple Columns using Tablefunc

高级用法:
Dynamic alternative to pivot with CASE and GROUP BY

设置

CREATE TEMP TABLE tbl
   (id int, extra_info varchar(3), month date, value int);

INSERT INTO tbl (id, extra_info, month, value)
VALUES
   (1, 'abc', '2012-01-01', 10),
   (1, 'abc', '2012-02-01', 20),
   (2, 'def', '2012-01-01', 10),
   (2, 'def', '2012-02-01', 5),
   (1, 'abc', '2012-01-01', 15),
   (3, 'ghi', '2012-03-01', 15);

我在基表中使用实际的date,因为我假设只是为了简化你的问题而隐藏它。但只有月份名称,ORDER BY就没有了。

查询

SELECT * FROM crosstab(
     $$SELECT id, extra_info, to_char(month, 'mon'), sum(value) AS value
       FROM   tbl
       GROUP  BY 1,2,month
       ORDER  BY 1,2,month$$

    ,$$VALUES
      ('jan'::text), ('feb'), ('mar'), ('apr'), ('may'), ('jun')
    , ('jul'),       ('aug'), ('sep'), ('oct'), ('nov'), ('dec')$$
   )
AS ct (id  int, extra text
   , jan int, feb int, mar int, apr int, may int, jun int
   , jul int, aug int, sep int, oct int, nov int, dec int);

结果:

 id | extra | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec
----+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----
  1 | abc   |  25 |  20 |     |     |     |     |     |     |     |     |     |
  2 | def   |  10 |   5 |     |     |     |     |     |     |     |     |     |
  3 | ghi   |     |     |  15 |     |     |     |     |     |     |     |     |

安装tablefunc模块需要一些开销和一些学习,但结果查询更快,更短,更通用。