【问题标题】:Postgress fill the missed row values with previous values during queryPostgres 在查询期间用以前的值填充丢失的行值
【发布时间】:2021-03-09 06:58:34
【问题描述】:

我有一些表有一些数据不存在的行,由于某些商业原因,我无法在那些日子里向用户显示 null 或 0,因此需要保留该表的先前值。

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')
        );

当我查询一个表时,我想要如下的数据,错过的日期应该加上前一个日期的值

 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

我正在使用 JPA 查询表

@Query(value = "SLECT * FROM Foo 其中 ID=:uid") 点亮 getFoo(String uid);

【问题讨论】:

    标签: sql postgresql datetime spring-data-jpa lateral-join


    【解决方案1】:

    递归 CTE 是一种非常简单的填补空白的方法,如下所示:

    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;
    

    它们使您可以轻松地从上一行中保留所需的值。

    不过,最有效的方法可能是使用generate_series()——但在每一行中:

    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 是一个 dbfiddle。

    【讨论】:

      【解决方案2】:

      你可以使用generate_series()来生成每个id的日期,然后横向join来带上对应的值:

      select x.id, f.foo_value, x.foo_date
      from (
          select f.id, x.foo_date
          from foo f
          cross join lateral generate_series(min(foo_date), max(food_date), '1 day') as x(foo_date)
          group by f.id
      ) x
      cross join lateral (
          select foo_value
          from foo f
          where f.id = x.id and f.foo_date <= x.foo_date
          order by f.foo_date desc
          limit 1
      ) f
      

      根据您的数据集,可能使用left join 和窗口函数来带来最后一个非null 值:

      select id, max(foo_value) over(partition by id, grp) as foo_value, foo_date
      from (
          select x.id, f.value, x.foo_date, 
              count(f.id) over(partition by x.id order by x.foo_date) grp
          from (
              select f.id, x.foo_date
              from foo f
              cross join lateral generate_series(min(foo_date), max(food_date), '1 day') as x(foo_date)
              group by f.id
          ) x
          left join foo on f.id = x.id and f.food_date = x.foo_date
      ) t
      

      【讨论】:

        猜你喜欢
        • 2018-04-14
        • 2020-12-07
        • 1970-01-01
        • 2023-03-25
        • 2021-10-11
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 2021-10-09
        相关资源
        最近更新 更多