当我期望多行时,Postgres中的交叉表函数返回一行输出

时间:2015-10-03 00:52:21

标签: sql postgresql pivot-table crosstab window-functions

我目前有一个以下格式的表id scenario period ct 2 1 1 1 2 1 2 1 2 1 3 1 2 1 4 1 2 2 1 1 2 2 2 1 2 2 3 1 2 2 4 1 2 3 1 1 2 3 2 1 2 3 3 1 2 3 4 1

id    scenario    period    1    2    3    4
2        1          1       1
2        1          2            1
2        1          3                 1
2        1          4                      1
2        2          1       1
2        2          2            1
2        2          3                 1
2        2          4                      1
2        3          1       1
2        3          2            1
2        3          3                 1
2        3          4                      1

我想创建下表:

crosstab()

已经在我的Postgres数据库中创建了tablefunc扩展。我目前正在尝试使用id scenario period 1 2 3 4 2 1 1 1 1 1 1 功能来完成支点。但是,我得到的表格如下所示:

SELECT * FROM crosstab(
        'SELECT id, scenario, period, ct FROM m 
            ORDER BY 1',
         'SELECT DISTINCT period FROM m 
            ORDER BY 1')
AS (id, scenario, period, 1, 2, 3, 4);

我试过的查询:

import csv

def parse_file(DATAFILE, lines):
    with open(DATAFILE, 'r') as fd:
        dat    = csv.reader(fd)
        header = next(dat) # makes strong assumption that csv has header
        retval = list()

        for index, row in enumerate(dat):
            if (index >= lines): break # restricts number of lines
            retval.append(dict(zip(header, row)))

    return retval

d = parse_file(DATAFILE, 10)

1 个答案:

答案 0 :(得分:4)

此查询生成所需的输出:

SELECT id, scenario, period, p1, p2, p3, p4  -- all except aux column rn
FROM   crosstab(
  'SELECT row_number() OVER (ORDER BY id, scenario, period)::int AS rn
        , id, scenario, period, period, ct
   FROM   m
   ORDER  BY 1'
, 'VALUES (1), (2), (3), (4)'
   ) AS (rn int, id int, scenario int, period int, p1 int, p2 int, p3 int, p4 int);

两个特殊困难:

  1. 您还没有 row_name 的唯一列。我使用row_number()生成代理键:rn。我将其从外部SELECT移除,以匹配您想要的结果 您尝试的方式,id被视为 row_name ,所有输入行都聚合到一个输出行。

  2. 您希望结果中包含其他列(scenarioperiod),这些列必须位于 row_name 之后和类别之前。您必须列出period 两次以额外获取原始列 - 多余,就好像看起来一样。

  3. 基础:

    与此特定案例相关:

    通常,您会遇到如下查询:

    SELECT id, scenario, p1, p2, p3, p4  -- all except aux column rn
    FROM   crosstab(
      'SELECT rank() OVER (ORDER BY id, scenario)::int AS rn
            , id, scenario, period, ct
       FROM   m
       ORDER  BY 1'
    , 'VALUES (1), (2), (3), (4)'
       ) AS (rn int, id int, scenario int, p1 int, p2 int, p3 int, p4 int);
    

    输出如下:

    id   scenario   p1   p2   p3   p4
    2    1          1    1    1    1
    2    2          1    1    1    1
    2    3          1    1    1    1
    

    请注意,使用rank()代替row_number()(id, scenario)的相同组合组合在一起。
    如果计数不是全部1,则结果更有意义。