按多列

时间:2017-06-14 22:07:06

标签: sql postgresql

我有一个带有id的实体表,以及一个类别(几个不同的值允许使用NULL)来自3个不同年份(类别可以从1年到另一年不同),采用“宽”表格格式:

| ID  | CATEG_Y1 | CATEG_Y2 | CATEG_Y3 |
+-----+----------+----------+----------+
| 1   | NULL     | B        | C        |
| 2   | A        | A        | C        |
| 3   | B        | A        | NULL     |
| 4   | A        | C        | B        |
| ... | ...      | ...      | ...      |

我想简单地按类别计算实体数量,按类别分组,单独计算年份:

+-------+----+----+----+
| CATEG | Y1 | Y2 | Y3 |
+-------+----+----+----+
| A     | 6  | 4  |  5 | <- 6 entities w/ categ_y1, 4 w/ categ_y2, 5 w/ categ_y3  
| B     | 3  | 1  | 10 |
| C     | 8  | 4  |  5 |
| NULL  | 3  | 3  |  3 |
+-------+----+----+----+

我想我可以通过将值分组到另一列并UNION ALL结果来实现,但我想知道是否有更快速的&amp;方便的方式,如果我有更多的列/年可以管理(例如20-30个不同的值),它是否可以推广

3 个答案:

答案 0 :(得分:1)

有点笨拙,但可能有人有更好的主意。查询首先收集所有不同的类别(from部分中的union-query),然后使用select部分中的专用子查询计算出现的次数。如果已经有一个表已经定义了可用的类别,那么可以省略union-part(我假设categ_y1是这种主类别表的外键)。希望没有很多拼写错误:

select categories.cat,
       (select count(categ_y1) from table ty1 where select categories.cat = categ_y1) as y1,
       (select count(categ_y2) from table ty2 where select categories.cat = categ_y2) as y2,
       (select count(categ_y3) from table ty3 where select categories.cat = categ_y3) as y3
from ( select categ_y1 as cat from table t1
 union select categ_y2 as cat from table t2
 union select categ_y3 as cat from table t3) categories 

答案 1 :(得分:1)

使用jsonb函数将数据(来自问题)转换为以下格式:

select categ, jsonb_object_agg(key, count) as jdata
from (
    select value as categ, key, count(*)
    from my_table t,
    jsonb_each_text(to_jsonb(t)- 'id')
    group by 1, 2
    ) s
group by 1
order by 1;

 categ |                     jdata                     
-------+-----------------------------------------------
 A     | {"categ_y1": 2, "categ_y2": 2}
 B     | {"categ_y1": 1, "categ_y2": 1, "categ_y3": 1}
 C     | {"categ_y2": 1, "categ_y3": 2}
       | {"categ_y1": 1, "categ_y3": 1}
(4 rows)

对于已知(静态)年数,您可以轻松解压缩jsonb列:

select categ, jdata->'categ_y1' as y1, jdata->'categ_y2' as y2, jdata->'categ_y3' as y3 
from (
    select categ, jsonb_object_agg(key, count) as jdata
    from (
        select value as categ, key, count(*)
        from my_table t,
        jsonb_each_text(to_jsonb(t)- 'id')
        group by 1, 2
        ) s
    group by 1
    ) s
order by 1;

 categ | y1 | y2 | y3 
-------+----+----+----
 A     | 2  | 2  | 
 B     | 1  | 1  | 1
 C     |    | 1  | 2
       | 1  |    | 1
(4 rows)    

要获得完全动态的解决方案,您可以使用Flatten aggregated key/value pairs from a JSONB field中描述的函数create_jsonb_flat_view()

答案 2 :(得分:1)

我会这样做,因为使用union all后跟聚合:

select categ, sum(categ_y1) as y1, sum(categ_y2) as y2,
       sum(categ_y3) as y3
from ((select categ_y1, 1 as categ_y1, 0 as categ_y2, 0 as categ_y3
       from t
      ) union all
      (select categ_y2, 0 as categ_y1, 1 as categ_y2, 0 as categ_y3
       from t
      ) union all
      (select categ_y3, 0 as categ_y1, 0 as categ_y2, 1 as categ_y3
       from t
      )
     )
group by categ ;