【问题标题】:Sum by month and put months as columns按月求和并将月份作为列
【发布时间】:2013-05-11 23:26:19
【问题描述】:

背景

我有每月的时间序列数据,我想对每个 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 大师的任何想法!

【问题讨论】:

    标签: sql postgresql pivot case crosstab


    【解决方案1】:

    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 模块需要一些开销和一些学习,但生成的查询更快、更短、更通用。

    【讨论】:

    • 我知道你会带着crosstab 的答案进来。 :)
    • @bluefeet:如果我没记错的话,我们以前也遇到过这种情况。 :)
    • 具有讽刺意味的是,我们确实看过 Crosstab,它看起来相当复杂,所以我在这里询问希望有一种通用的方式。现在我知道共识似乎是普遍的:P
    【解决方案2】:

    您可以使用带有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

    【讨论】:

    • 谢谢!我依稀记得以前见过这样的东西,希望我的大脑更可靠!有没有办法在不手动处理案件的情况下做到这一点?几个月都很好用,但我想知道是否有更通用的表单可以应用于具有 100 个条目的类别?
    • @PeterHamilton 是的,您可以使用crosstab 函数。这是另一个用户的一个很好的答案——stackoverflow.com/questions/15506199/…
    • 正是我需要的!有一个版本每个月都使用子查询,使用它需要几个小时来构建 verse seconds。
    猜你喜欢
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多