Postgres 在查询期间用以前的值填充丢失的行值
Postgress fill the missed row values with previous values during query
我有一些 table 有一些数据不存在的行,由于某些商业原因,我无法在那些日子里向用户显示 null 或 0,所以需要保留以前的值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')
);
我想在查询table时得到如下数据,错过的日期应该加上前一个日期的值
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 查询 table
@Query(value = "SLECT * FROM Foo where ID=:uid")
Lits getFoo(String uid);
可以用generate_series()
生成每个id
的日期,然后一个lateral 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
和 window 函数带来最后一个非 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
递归 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 是一个 db<>fiddle.
我有一些 table 有一些数据不存在的行,由于某些商业原因,我无法在那些日子里向用户显示 null 或 0,所以需要保留以前的值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')
);
我想在查询table时得到如下数据,错过的日期应该加上前一个日期的值
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 查询 table
@Query(value = "SLECT * FROM Foo where ID=:uid") Lits getFoo(String uid);
可以用generate_series()
生成每个id
的日期,然后一个lateral 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
和 window 函数带来最后一个非 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
递归 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 是一个 db<>fiddle.