Историческая зависимость Postgres для поля с произвольным LAG - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть таблица 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

Где период - это просто отметка времени года-месяца как 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

Я нашел способ обойти это, но это, по сути, связано с созданием временной таблицы со всеми периодами, где flag = 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;

Но я надеялся, что мы сможем сделать это лучше, поскольку таблицы, на которые мы смотрим, довольно большие.

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Вы ищите условный максимум:

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;
0 голосов
/ 05 сентября 2018

Если таблица имеет правильные индексы, вы можете использовать коррелированный подзапрос:

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