【问题标题】:Postgres historical dependency for field with arbitrary LAG具有任意 LAG 的字段的 Postgres 历史依赖性
【发布时间】:2018-09-05 15:26:59
【问题描述】:

我有一个如下所示的 postgres 表:

 refnum | period | flag 
--------+--------+------
 ref01  | 201701 |    0
 ref01  | 201702 |    0
 ref01  | 201703 |    1
 ref01  | 201704 |    0
 ref01  | 201705 |    0
 ref01  | 201706 |    1
 ref01  | 201707 |    0

其中 period 基本上只是一个 int 形式的年月时间戳。我想这样做,以便我们添加一个列来维护标志 = 1 的最后一个时期。所以它应该在最后看起来像这样:

 refnum | period | flag | lastPeriod
--------+--------+------+------------
 ref01  | 201701 |    0 | NULL
 ref01  | 201702 |    0 | NULL
 ref01  | 201703 |    1 | 201703
 ref01  | 201704 |    0 | 201703
 ref01  | 201705 |    0 | 201703
 ref01  | 201706 |    1 | 201706
 ref01  | 201707 |    0 | 201706

因此,每一行的 lastPeriod 列都取决于前一行中该列的具体化值。我尝试使用窗口函数和 LAG 来执行此操作,但这仅在您知道要回顾的行数时才有效。如果窗口中的每一行都在前一行之后进行评估,那就太好了,但似乎它们是独立于之前的值运行的。我基本上想要的是类似的东西:

SELECT CASE WHEN current_row.flag = 1 THEN current_row.period ELSE prev_row.lastPeriod

我想出了解决这个问题的一种方法,但它基本上涉及创建一个临时表,其中包含标志 = 1 的所有时段,加入该表,然后拉取最大值:

select refnum, period, max(backfill) FROM 
(
    select refnum, a.period as period, b.period as backfill
    FROM my_table a
    LEFT JOIN tmp_periods b ON a.period >= b.period
) as foo group by refnum, period order by period;

但我希望我们能以更好的方式做到这一点,因为我们正在查看的表格非常大。

【问题讨论】:

    标签: sql postgresql window-functions


    【解决方案1】:

    你关心寻找一个有条件的最大值:

    select t.*,
           max(case when flag = 1 then period end) over (partition by refnum order by period) as lastperiod
    from t;
    

    更多最新版本的 Postgres 支持 filter:

    select t.*,
           max(period) filter (where flag = 1) over (partition by refnum order by period)
    from t;
    

    【讨论】:

    • 天哪,太棒了!我想不出将条件逻辑传递给这些窗口/聚合函数的方法。
    【解决方案2】:

    如果表有正确的索引,那么你可以使用 correlated 子查询:

    select t.*, (case when flag = 1 then period 
                      when flag = 0 
                      then (select t1.period 
                            from table t1 
                            where t1.refnum = t.refnum and 
                                  t1.period < t.period and t1.flag = 1 
                            order by t1.period desc
                            limit 1
                           ) 
                 end) as lastPeriod
    from table t;
    

    【讨论】:

    • 这是一个很好的策略。我将不得不对我们的完整数据集进行一些测试,因为可能会在效率方面进行权衡。
    猜你喜欢
    • 2021-07-20
    • 1970-01-01
    • 2015-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多