Заполнить строку предыдущим значением

Как в таблице заполнить следующие строки, у которых null последним не нулевым значением?

       sum1 |     sum2 |           date1     |     date2           | total 
------------+----------+---------------------+---------------------+-------
         51 |       10 | 2016-12-31 00:00:00 | 2016-12-31 00:00:00 |   510
         49 |       20 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 |   980
         50 |       20 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 |  1000
            |       30 |                     | 2017-01-02 00:00:00 |      
            |       40 |                     | 2017-01-03 00:00:00 |      
            |       50 |                     | 2017-01-04 00:00:00 |      
            |       60 |                     | 2017-01-06 00:00:00 |      
         55 |          | 2017-01-10 00:00:00 |                     |      
         51 |        5 | 2017-01-11 00:00:00 | 2017-01-11 00:00:00 |   255
         52 |        5 | 2017-01-11 00:00:00 | 2017-01-11 00:00:00 |   260
         53 |        6 | 2017-01-12 00:00:00 | 2017-01-12 00:00:00 |   318
         53 |        5 | 2017-01-12 00:00:00 | 2017-01-12 00:00:00 |   265
         54 |          | 2017-01-13 00:00:00 |                     |      

Т.е. если в sum1 у меня null, то я его заполняю последним значением из предыдущей строки:

       sum1 |     sum2 |           date1     |     date2           | total 
------------+----------+---------------------+---------------------+-------
         51 |       10 | 2016-12-31 00:00:00 | 2016-12-31 00:00:00 |   510
         49 |       20 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 |   980
         50 |       20 | 2017-01-01 00:00:00 | 2017-01-01 00:00:00 |  1000
         50 |       30 |                     | 2017-01-02 00:00:00 |  1500
         50 |       40 |                     | 2017-01-03 00:00:00 |  2000    
         50 |       50 |                     | 2017-01-04 00:00:00 |  2500
         50 |       60 |                     | 2017-01-06 00:00:00 |  3000
         55 |          | 2017-01-10 00:00:00 |                     |      
         51 |        5 | 2017-01-11 00:00:00 | 2017-01-11 00:00:00 |   255
         52 |        5 | 2017-01-11 00:00:00 | 2017-01-11 00:00:00 |   260
         53 |        6 | 2017-01-12 00:00:00 | 2017-01-12 00:00:00 |   318
         53 |        5 | 2017-01-12 00:00:00 | 2017-01-12 00:00:00 |   265
         54 |          | 2017-01-13 00:00:00 |                     |      

Для того, чтобы конкретизировать вопрос, я заменю исходную таблицу на следующую:

 course_sum | trxn_sum |        date         | total 
------------+----------+---------------------+-------
         51 |       10 | 2016-12-31 00:00:00 |   510
         49 |       20 | 2017-01-01 00:00:00 |   980
         50 |       20 | 2017-01-01 00:00:00 |  1000
            |       30 | 2017-01-02 00:00:00 |      
            |       40 | 2017-01-03 00:00:00 |      
            |       50 | 2017-01-04 00:00:00 |      
            |       60 | 2017-01-06 00:00:00 |      
         55 |          | 2017-01-10 00:00:00 |      
         51 |        5 | 2017-01-11 00:00:00 |   255
         52 |        5 | 2017-01-11 00:00:00 |   260
         53 |        6 | 2017-01-12 00:00:00 |   318
         53 |        5 | 2017-01-12 00:00:00 |   265
         54 |          | 2017-01-13 00:00:00 |      

Сортировка должна происходить по date.


Ответы (1 шт):

Автор решения: Ainar-G

(Ответ является модифицированным переводом вот этого ответа с английского сайта. Возможно, есть более элегантный способ, но я его не нашёл, а этот работает.)

Ещё более упрощённая версия вашей таблицы для ясности примера:

 sum1 |         date1
------+------------------------
 NULL | 2020-01-01 00:00:00+00
    1 | 2020-01-01 00:00:01+00
    2 | 2020-01-01 00:00:02+00
 NULL | 2020-01-01 00:00:03+00
 NULL | 2020-01-01 00:00:04+00
 NULL | 2020-01-01 00:00:05+00
    5 | 2020-01-01 00:00:06+00

Есть техника, заключающаяся в том, что сначала вы получаете колонку, которая возрастает на 1 каждый раз, когда значение ненулевое:

SELECT sum1
     , date1
     , SUM(CAST(sum1 IS NOT NULL AS INTEGER))
       OVER (ORDER BY date1) AS inc
  FROM t_1
;
 sum1 |         date1          | inc
------+------------------------+-----
 NULL | 2020-01-01 00:00:00+00 |   0
    1 | 2020-01-01 00:00:01+00 |   1
    2 | 2020-01-01 00:00:02+00 |   2
 NULL | 2020-01-01 00:00:03+00 |   2
 NULL | 2020-01-01 00:00:04+00 |   2
 NULL | 2020-01-01 00:00:05+00 |   2
    5 | 2020-01-01 00:00:06+00 |   3

После чего второй запрос просто забирает первое значение в группе с этим inc, плюс значение по умолчанию 0 на случай, если первое значение в оригинальной таблице является нулевым:

WITH t_2 AS (
  SELECT sum1
       , date1
       , SUM(CAST(sum1 IS NOT NULL AS INTEGER))
         OVER (ORDER BY date1) AS inc
    FROM t_1
)
SELECT COALESCE(
         sum1
       , FIRST_VALUE(sum1) OVER (PARTITION BY inc)
       , 0
       ) AS sum1
     , date1
  FROM t_2
 ORDER BY date1
;
 sum1 |         date1
------+------------------------
    0 | 2020-01-01 00:00:00+00
    1 | 2020-01-01 00:00:01+00
    2 | 2020-01-01 00:00:02+00
    2 | 2020-01-01 00:00:03+00
    2 | 2020-01-01 00:00:04+00
    2 | 2020-01-01 00:00:05+00
    5 | 2020-01-01 00:00:06+00
→ Ссылка