【问题标题】:Crosstab function in Postgres returning a one row output when I expect multiple rows当我期望多行时,Postgres 中的交叉表函数返回单行输出
【发布时间】:2015-12-31 07:00:39
【问题描述】:

我目前有一个表格m,格式如下:

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

已经在我的 Postgres 数据库中创建了 tablefunc 扩展。我目前正在尝试使用crosstab() 函数来完成数据透视。但是,我得到的表格如下所示:

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);

【问题讨论】:

  • 您提供的查询完全无效。 (列定义列表缺少数据类型)。您尝试的实际查询是什么?
  • 我的数据集要大得多,但我想展示一个更简单的示例。我相信更正确的查询是: SELECT * FROM crosstab('SELECT id, scenario, period, ct FROM m ORDER BY 1', 'SELECT DISTINCT period FROM m ORDER BY 1') AS (i​​d int, scenario int, period整数,“1”整数,“2”整数,“3”整数,“4”整数);

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


【解决方案1】:

此查询生成您想要的输出:

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 之后和 category 之前。您必须列出 period 两次 才能额外获得原始列 - 看起来是多余的。

基础知识:

与这个特殊案例有关:


通常,您会有这样的查询:

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,则结果更有意义。

【讨论】:

  • 您好欧文,感谢您回答我的问题!我有一个关于交叉表功能的后续问题。事实证明,我希望我的交叉表函数通过从另一个表中选择 ID 来指定值列(交叉表函数的第二个参数是“SELECT id_prod FROM prod;”)。如何更改查询的结尾(在 AS (rn int,... ) 之后)以正确显示此更改的结果?
猜你喜欢
  • 1970-01-01
  • 2020-06-26
  • 2016-08-25
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多