Postgres - 带交叉表的部分枢轴

Postgres - partial pivot with crosstab

我正在苦苦挣扎,知道搜​​索答案的术语可能是我的问题,因为我无法想象这是一个边缘案例。

dbfiddle available

我在 Postgres 9.4 中有一个 table:

CREATE TABLE test (
    id serial PRIMARY KEY, cust_id INTEGER,
    category VARCHAR, key INTEGER, value INTEGER
); 
INSERT INTO test (cust_id, category, key, value)
VALUES
    (1, 'alpha', 0,300),(1, 'bravo', 0,150),(1, 'alpha', 1,300),
    (1, 'bravo', 1,200),(1, 'alpha', 2,300),(1, 'bravo', 2,250),
    (2, 'alpha', 0,301),(2, 'bravo', 0,151),(2, 'alpha', 1,301),
    (2, 'bravo', 1,201),(2, 'alpha', 2,301),(2, 'bravo', 2,251),
    (3, 'alpha', 0,302),(3, 'bravo', 0,152),(3, 'alpha', 1,302),
    (3, 'bravo', 1,202),(3, 'alpha', 2,302),(3, 'bravo', 2,252);


 id | cust_id | category | key | value 
----+---------+----------+-----+-------
  1 |       1 | alpha    |   0 |   300
  2 |       1 | bravo    |   0 |   150
  3 |       1 | alpha    |   1 |   300
  4 |       1 | bravo    |   1 |   200
  5 |       1 | alpha    |   2 |   300
  6 |       1 | bravo    |   2 |   250
  7 |       2 | alpha    |   0 |   301
  8 |       2 | bravo    |   0 |   151
  9 |       2 | alpha    |   1 |   301
 10 |       2 | bravo    |   1 |   201
 11 |       2 | alpha    |   2 |   301
 12 |       2 | bravo    |   2 |   251
 13 |       3 | alpha    |   0 |   302
 14 |       3 | bravo    |   0 |   152
 15 |       3 | alpha    |   1 |   302
 16 |       3 | bravo    |   1 |   202
 17 |       3 | alpha    |   2 |   302
 18 |       3 | bravo    |   2 |   252
(18 rows)

我想查询如下结果:

 cust_id | category |  0  |  1  |  2  
---------+----------+-----+-----+-----
       1 | alpha    | 300 | 300 | 300 
       1 | bravo    | 150 | 200 | 250 
       2 | alpha    | 301 | 301 | 301
       2 | bravo    | 151 | 201 | 251
       3 | alpha    | 302 | 302 | 302
       3 | bravo    | 152 | 202 | 252
(6 rows)

我试过:

SELECT
    *
FROM
    crosstab(
        'SELECT cust_id,category,key,value FROM test ORDER BY cust_id,category,key',
        $$values ('0'::INT),
        ('1'::INT),
        ('2'::INT) $$
    ) AS ct (
        "cust_id" INT, "category" TEXT, "0" INT,
        "1" INT, "2" INT
    );

这让我受益匪浅(缺少 bravo 类别行并为第 1、2、3 列使用 bravo 值):

 cust_id | category |  0  |  1  |  2  
---------+----------+-----+-------
       1 | alpha    | 150 | 200 | 250 
       2 | alpha    | 151 | 201 | 251
       3 | alpha    | 152 | 202 | 252
(2 rows)

我通过删除 cust_id 字段并限制为单个 id 来更接近以下内容:

SELECT
    *
FROM
    crosstab(
        'SELECT category,key,value FROM test WHERE cust_id = 1 ORDER BY category,key',
        $$values ('0'::INT),
        ('1'::INT),
        ('2'::INT) $$
    ) AS ct (
        "category" TEXT, "0" INT,
        "1" INT, "2" INT
    );

但这只给出了单个 cust_id 的结果,但我需要为所有客户提供此结果:

 category |  0  |  1  |  2  
----------+-----+-------
 alpha    | 300 | 300 | 300 
 bravo    | 150 | 200 | 250 
(2 rows)

https://dbfiddle.uk/?rdbms=postgres_9.5&fiddle=2c75cf9a1b18bb980ddd72953235d54e

这是一种方法:

select cust_id , category  
    , max(case when key = 0 then value end) "0" 
    , max(case when key = 1 then value end) "1"
    , max(case when key = 2 then value end) "2"
from test
group by cust_id , category
order by cust_id , category