【问题标题】:PostgreSQL crosstab doesn't work as desiredPostgreSQL 交叉表无法按预期工作
【发布时间】:2023-03-14 03:49:01
【问题描述】:

在此示例中,我希望生成的数据透视表具有 4 列的值,但实际上只有 2 列的值。

它应该返回如下内容:

| time | trace1 | trace2 | trace3 | trace4 |
| -----------------------------------------|
|   t  |   v    |   v    |   v    |   v    |
|   t  |   v    |   v    |   v    |  null  |
|   t  |  null  |   v    |   v    |   v    |
|   t  |   v    |   v    |  null  |   v    |
|   t  |   v    |  null  |   v    |   v    |
|------------------------------------------|

但我得到了这个:

| time | trace1 | trace2 | trace3 | trace4 |
| -----------------------------------------|
|   t  |   v    |   v    |  null  |  null  |
|   t  |   v    |   v    |  null  |  null  |
|   t  |   v    |   v    |  null  |  null  |
|   t  |   v    |  null  |  null  |  null  |
|   t  |   v    |  null  |  null  |  null  |
|------------------------------------------|

更糟糕的是,如果我删除了

order by unixdatetime

,所有内容都将被粉碎成一列,如下所示:

| time | trace1 | trace2 | trace3 | trace4 |
| -----------------------------------------|
|   t  |   v    |  null  |  null  |  null  |
|   t  |   v    |  null  |  null  |  null  |
|   t  |   v    |  null  |  null  |  null  |
|   t  |   v    |  null  |  null  |  null  |
|   t  |   v    |  null  |  null  |  null  |
|------------------------------------------|

代码如下:

select * 
from crosstab(
    $$
    select 
        unixdatetime, 
        gaugesummaryid, 
        value::double precision 
    from 
    (values
        (1546300800,187923,1.5),
        (1546387200,187923,1.5),
        (1546473600,187923,1.5),
        (1546560000,187923,1.75),
        (1546646400,187923,1.75),
        (1546732800,187923,1.75),
        (1546819200,187923,1.75),
        (1546905600,187923,1.5),
        (1546992000,187923,1.5),
        (1547078400,187923,1.5),
        (1547164800,187923,1.5),
        (1547337600,187924,200),
        (1547424000,187924,200),
        (1547510400,187924,200),
        (1547596800,187924,200),
        (1547683200,187924,200),
        (1547769600,187924,200),
        (1547856000,187924,200),
        (1547942400,187924,200),
        (1548028800,187924,200),
        (1548115200,187924,200),
        (1548201600,187924,200),
        (1548288000,187924,200),
        (1546300800,187926,120),
        (1546387200,187926,120),
        (1546473600,187926,120),
        (1546560000,187926,110),
        (1546646400,187926,110),
        (1546732800,187926,110),
        (1546819200,187926,110),
        (1546905600,187926,115),
        (1546992000,187926,115),
        (1547078400,187926,115),
        (1547942400,187927,100),
        (1548028800,187927,100),
        (1548115200,187927,100),
        (1548201600,187927,100),
        (1548288000,187927,100)
    ) as t (unixdatetime, gaugesummaryid, value)
    order by unixdatetime
    $$
    ) as final_result (
        unixdatetime int, 
        trace1 double precision, 
        trace2 double precision, 
        trace3 double precision, 
        trace4 double precision
        );

如果你想玩,这里是链接:

https://dbfiddle.uk/?rdbms=postgres_11&fiddle=2c4f6098fb89b78898ba1bf6afa7f439

如何得到想要的结果?

【问题讨论】:

    标签: postgresql pivot crosstab


    【解决方案1】:

    我建议您使用filter (where ...) 子句而不是数据透视表。

    select
        unixdatetime,
        min(value) filter (where gaugesummaryid = 187923) as trace_1,
        min(value) filter (where gaugesummaryid = 187924) as trace_2,
        min(value) filter (where gaugesummaryid = 187926) as trace_3,
        min(value) filter (where gaugesummaryid = 187927) as trace_4
    from table
    group by 1;
    

    请注意,您必须使用聚合函数才能使用该子句。在您的情况下,使用minmaxavgsum 都没有关系。

    【讨论】:

      【解决方案2】:

      使用crosstab函数的2-argument form

      SELECT * 
      FROM crosstab(
              $$
              SELECT 
                      unixdatetime, 
                      gaugesummaryid, 
                      value::double precision 
              FROM test
              ORDER BY unixdatetime
              $$
              , 'SELECT DISTINCT gaugesummaryid FROM test ORDER BY 1 LIMIT 4'
              ) as final_result (
                      unixdatetime int, 
                      trace1 double precision, 
                      trace2 double precision, 
                      trace3 double precision, 
                      trace4 double precision
                      )
      

      产量

      | unixdatetime | trace1 | trace2 | trace3 | trace4 |
      |--------------+--------+--------+--------+--------|
      |   1546300800 |    1.5 |        |    120 |        |
      |   1546387200 |    1.5 |        |    120 |        |
      |   1546473600 |    1.5 |        |    120 |        |
      |   1546560000 |   1.75 |        |    110 |        |
      |   1546646400 |   1.75 |        |    110 |        |
      |   1546732800 |   1.75 |        |    110 |        |
      |   1546819200 |   1.75 |        |    110 |        |
      |   1546905600 |    1.5 |        |    115 |        |
      |   1546992000 |    1.5 |        |    115 |        |
      |   1547078400 |    1.5 |        |    115 |        |
      |   1547164800 |    1.5 |        |        |        |
      |   1547337600 |        |    200 |        |        |
      |   1547424000 |        |    200 |        |        |
      |   1547510400 |        |    200 |        |        |
      |   1547596800 |        |    200 |        |        |
      |   1547683200 |        |    200 |        |        |
      |   1547769600 |        |    200 |        |        |
      |   1547856000 |        |    200 |        |        |
      |   1547942400 |        |    200 |        |    100 |
      |   1548028800 |        |    200 |        |    100 |
      |   1548115200 |        |    200 |        |    100 |
      |   1548201600 |        |    200 |        |    100 |
      |   1548288000 |        |    200 |        |    100 |
      

      使用此设置:

      DROP TABLE IF EXISTS test;
      CREATE TABLE test (
              unixdatetime bigint, 
              gaugesummaryid int, 
              value double precision 
      );
      INSERT INTO test VALUES
              (1546300800,187923,1.5),
              (1546387200,187923,1.5),
              (1546473600,187923,1.5),
              (1546560000,187923,1.75),
              (1546646400,187923,1.75),
              (1546732800,187923,1.75),
              (1546819200,187923,1.75),
              (1546905600,187923,1.5),
              (1546992000,187923,1.5),
              (1547078400,187923,1.5),
              (1547164800,187923,1.5),
              (1547337600,187924,200),
              (1547424000,187924,200),
              (1547510400,187924,200),
              (1547596800,187924,200),
              (1547683200,187924,200),
              (1547769600,187924,200),
              (1547856000,187924,200),
              (1547942400,187924,200),
              (1548028800,187924,200),
              (1548115200,187924,200),
              (1548201600,187924,200),
              (1548288000,187924,200),
              (1546300800,187926,120),
              (1546387200,187926,120),
              (1546473600,187926,120),
              (1546560000,187926,110),
              (1546646400,187926,110),
              (1546732800,187926,110),
              (1546819200,187926,110),
              (1546905600,187926,115),
              (1546992000,187926,115),
              (1547078400,187926,115),
              (1547942400,187927,100),
              (1548028800,187927,100),
              (1548115200,187927,100),
              (1548201600,187927,100),
              (1548288000,187927,100);
      

      【讨论】:

        【解决方案3】:

        虽然可能缺少某些目标值,但您需要 crosstab() (like unutbu provided) 的 2 参数形式。
        但是使用产生不稳定结果的查询作为第二个参数是没有意义的。使用VALUES 表达式(或类似表达式)提供与生成的列定义列表同步的一组稳定的目标列。喜欢:

        SELECT * 
        FROM   crosstab(
           $$
           SELECT *
           FROM  (
              VALUES
              (bigint '1546300800', 187923, float8 '1.5')
            , (1546387200,187923,1.5)
            , (1546473600,187923,1.5)
         -- , ...
            , (1548288000,187927,100)
           ) t (unixdatetime, gaugesummaryid, value)
           ORDER BY 1,2
           $$
         , 'VALUES (187923), (187924), (187926), (187927)'    -- !!
           ) final_result (unixdatetime int
                         , trace1 float8
                         , trace2 float8
                         , trace3 float8
                         , trace4 float8);

        db小提琴here

        详细解释:

        如果从单个查询中获得 动态 数量的目标列的结果会很好。唉,SQL 不能那样工作。有各种解决方法。见:

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-24
          • 1970-01-01
          • 2020-09-10
          • 2013-12-23
          • 2014-12-09
          • 2016-01-13
          相关资源
          最近更新 更多