使用Tablefunc在多列上进行透视

ide*_*tor 11 sql postgresql pivot crosstab

有没有人过去常常使用tablefunc多个变量,而不是只使用行名文档说明:

对于具有相同row_name值的所有行,"额外"列应该是相同的.

我不知道如何在没有组合我想要转向的列的情况下如何做到这一点(我非常怀疑它会给我我需要的速度).一种可能的方法是将实体设为数字,并将其作为毫秒添加到localt,但这似乎是一种不稳定的方式.

我编辑了回答这个问题的数据:PostgreSQL Crosstab Query.

 CREATE TEMP TABLE t4 (
  timeof   timestamp
 ,entity    character
 ,status    integer
 ,ct        integer);

 INSERT INTO t4 VALUES 
  ('2012-01-01', 'a', 1, 1)
 ,('2012-01-01', 'a', 0, 2)
 ,('2012-01-02', 'b', 1, 3)
 ,('2012-01-02', 'c', 0, 4);

 SELECT * FROM crosstab(
     'SELECT timeof, entity, status, ct
      FROM   t4
      ORDER  BY 1,2,3'
     ,$$VALUES (1::text), (0::text)$$)
 AS ct ("Section" timestamp, "Attribute" character, "1" int, "0" int);
Run Code Online (Sandbox Code Playgroud)

返回:

 Section                   | Attribute | 1 | 0
---------------------------+-----------+---+---
 2012-01-01 00:00:00       |     a     | 1 | 2
 2012-01-02 00:00:00       |     b     | 3 | 4

因此,正如文档所述,对于每个行名称 'Section',假设额外的列'Attribute'是相同的.因此,它报告第二行的b,即使"实体"也具有该'timeof'值的'c '值.

期望的输出:

Section                   | Attribute | 1 | 0
--------------------------+-----------+---+---
2012-01-01 00:00:00       |     a     | 1 | 2
2012-01-02 00:00:00       |     b     | 3 |  
2012-01-02 00:00:00       |     c     |   | 4
Run Code Online (Sandbox Code Playgroud)

有什么想法或参考?

更多背景:我可能需要为数十亿行执行此操作,并且我正在测试以长格式和宽格式存储这些数据,并且看看我是否可以tablefunc比使用常规聚合函数更有效地使用从长格式到宽格式.
对于大约300个实体,我每分钟大约进行100次测量.通常,我们需要比较给定实体的给定秒的不同测量值,因此我们需要经常使用宽格式.此外,对特定实体进行的测量也是高度可变的.

编辑:我找到了一个资源:http://www.postgresonline.com/journal/categories/24-tablefunc.

Erw*_*ter 11

您的查询的问题在于b并且c共享相同的时间戳2012-01-02 00:00:00,并且您在查询中首先拥有该timestamptimeof,因此 - 即使您添加了大胆的重点 - b并且c只是属于同一组的额外列2012-01-02 00:00:00.b(引用手册)开始只返回第一个():

row_name列必须是第一个.在categoryvalue列必须是最后两列的顺序.row_name和之间的任何列category都被视为"额外".对于具有相同值的所有行,"额外"列应该是相同row_name.

大胆强调我的.
只需恢复前两列的顺序以entity生成行名称,它可以按需运行:

SELECT * FROM crosstab(
      'SELECT entity, timeof, status, ct
       FROM   t4
       ORDER  BY 1'
      ,'VALUES (1), (0)')
 AS ct (
    "Attribute" character
   ,"Section" timestamp
   ,"status_1" int
   ,"status_0" int);
Run Code Online (Sandbox Code Playgroud)

entity 当然,必须是独一无二的.

重申

  • row_name 第一
  • (可选)extra下一步
  • category(由第二个参数定义)和value 最后一个.

从每个分区的第一行填充额外的列row_name.忽略其他行中的值,每个row_name填充只有一列.通常那些对于每一行都是相同的row_name,但这取决于你.

对于答案中的不同设置:

SELECT localt, entity
     , msrmnt01, msrmnt02, msrmnt03, msrmnt04, msrmnt05  -- , more?
FROM   crosstab(
        'SELECT dense_rank() OVER (ORDER BY localt, entity)::int AS row_name
              , localt, entity -- additional columns
              , msrmnt, val
         FROM   test
         -- WHERE  ???   -- instead of LIMIT at the end
         ORDER  BY localt, entity, msrmnt
         -- LIMIT ???'   -- instead of LIMIT at the end
     , $$SELECT generate_series(1,5)$$)  -- more?
     AS ct (row_name int, localt timestamp, entity int
          , msrmnt01 float8, msrmnt02 float8, msrmnt03 float8, msrmnt04 float8, msrmnt05 float8 -- , more?
            )
LIMIT 1000  -- ??!!
Run Code Online (Sandbox Code Playgroud)

难怪你的测试中的查询表现非常糟糕.测试设置有14M行,你处理所有他们投掷大部分逃脱之前LIMIT 1000.对于简化的结果集,在源查询中添加WHERE条件或LIMIT!

此外,您使用的阵列在它之上是不必要的昂贵.我使用dense_rank()生成代理行名称.

db <>在这里摆弄 - 使用更简单的测试设置和更少的行.


ide*_*tor 11

在我最初的问题中,我应该将它用于我的示例数据:

CREATE TEMP TABLE t4 (
 timeof    date
,entity    integer
,status    integer
,ct        integer);
INSERT INTO t4 VALUES 
 ('2012-01-01', 1, 1, 1)
,('2012-01-01', 1, 0, 2)
,('2012-01-01', 3, 0, 3)
,('2012-01-02', 2, 1, 4)
,('2012-01-02', 3, 1, 5)
,('2012-01-02', 3, 0, 6);
Run Code Online (Sandbox Code Playgroud)

有了这个,我必须转向时间和实体.由于tablefunc只使用一列进行旋转,因此您需要找到一种方法来填充该列中的两个维度.(http://www.postgresonline.com/journal/categories/24-tablefunc).我使用了数组,就像该链接中的示例一样.

SELECT (timestamp 'epoch' + row_name[1] * INTERVAL '1 second')::date 
           as localt, 
           row_name[2] As entity, status1, status0
FROM crosstab('SELECT ARRAY[extract(epoch from timeof), entity] as row_name,
                    status, ct
               FROM t4 
               ORDER BY timeof, entity, status'
     ,$$VALUES (1::text), (0::text)$$) 
          as ct (row_name integer[], status1 int, status0 int)
Run Code Online (Sandbox Code Playgroud)

FWIW,我尝试使用字符数组,到目前为止看起来这对我的设置更快; 9.2.3 Postgresql.

这是结果和期望的输出.

localt           | entity | status1 | status0
--------------------------+---------+--------
2012-01-01       |   1    |    1    |   2
2012-01-01       |   3    |         |   3
2012-01-02       |   2    |    4    |  
2012-01-02       |   3    |    5    |   6
Run Code Online (Sandbox Code Playgroud)

我很好奇这是如何在更大的数据集上执行的,并将在以后报告.