Skip to content
Advertisement

Postgress fill the missed row values with previous values during query

I have some table which has some rows where data is not exists, due to some biz reason I can not show the null or 0 to user during those days, so need to keep previous value of that table.

create table foo (ID VARCHAR(10), foo_value int, foo_date date);

insert into foo (
          values
          ('234534', 100, '2017-01-01'),
          ('234534', 200, '2017-01-02'),
          ('234534', 300, '2017-01-03'),
          ('234534', 180, '2017-01-08')
        );

I want the data like below when I query a table, the missed date should be added with value of previous date

 ID        | foo_value       | foo_date
-----------+-----------------+------------
 234534    | 100             | 2017-01-01
 234534    | 200             | 2017-02-01
 234534    | 300             | 2017-03-01
 234534    | 300             | 2017-04-01
 234534    | 300             | 2017-05-01
 234534    | 300             | 2017-06-01
 234534    | 180             | 2017-07-01

I am using JPA to query the table

@Query(value = “SLECT * FROM Foo where ID=:uid”) Lits getFoo(String uid);

Advertisement

Answer

Recursive CTEs are a pretty easy way to fill-in-the-gaps like this:

with recursive cte as (
      select f.id, f.foo_value, f.foo_date,
             lead(f.foo_date, 1, f.foo_date) over (partition by f.id order by f.foo_date) - interval '1 day' as next_date
      from foo f
      union all
      select cte.id, cte.foo_value, (cte.foo_date + interval '1 day')::date, cte.next_date
      from cte
      where cte.foo_date < cte.next_date
     )
select *
from cte;

They make it easy to retain the values you want from the previous row.

The most efficient method, though, is probably to use generate_series() — but within each row:

with  f as (
      select f.id, f.foo_value, f.foo_date,
         coalesce(lead(f.foo_date) over (partition by f.id order by f.foo_date) - interval '1 day', f.foo_date) as next_date
      from foo f
     )
select f.id, f.foo_value, gs.dte
from f left join lateral
     generate_series(f.foo_date, f.next_date, interval '1 day') gs(dte)

Here is a db<>fiddle.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement