在PostgreSQL中的时间表数据中添加丢失的每月日期



我在表中有每月的时间序列数据,其中日期是一个月的最后一天。数据中缺少一些日期。我想插入这些日期,并为其他属性添加零值。表如下:

id     report_date   price
1       2015-01-31    40
1       2015-02-28    56
1       2015-04-30    34
2       2014-05-31    45
2       2014-08-31    47

我想将此表转换为

id     report_date   price
1       2015-01-31    40
1       2015-02-28    56
1       2015-03-31    0
1       2015-04-30    34
2       2014-05-31    45
2       2014-06-30    0
2       2014-07-31    0
2       2014-08-31    47

有什么办法可以在PostgreSQL中做到这一点?目前,我们正在Python中这样做。随着我们的数据日复一日增长,仅处理I/O的效率仅用于一项任务。

谢谢

您可以使用generate_series()来生成日期,然后left join来引入值:

with m as (
      select id, min(report_date) as minrd, max(report_date) as maxrd
      from t
      group by id
     )
select m.id, m.report_date, coalesce(t.price, 0) as price 
from (select m.*, generate_series(minrd, maxrd, interval '1' month) as report_date
      from m
     ) m left join
     t
     on m.report_date = t.report_date;

编辑:

事实证明,上述时间不太有用,因为在月底增加几个月并不能保留该月的最后一天。

这很容易修复:

with t as (
      select 1 as id, date '2012-01-31' as report_date, 10 as price union all
      select 1 as id, date '2012-04-30', 20
     ), m as (
      select id, min(report_date) - interval '1 day' as minrd, max(report_date) - interval '1 day' as maxrd
      from t
      group by id
     )
select m.id, m.report_date, coalesce(t.price, 0) as price 
from (select m.*, generate_series(minrd, maxrd, interval '1' month) + interval '1 day' as report_date
      from m
     ) m left join
     t
     on m.report_date = t.report_date;

第一个CTE只是为了生成样本数据。

这对戈登的查询有所改进,在某些情况下,它无法获得一个月的最后一个日期。

本质上,您生成的所有月底日期介于每个ID的minmax日期(使用generate_series)和该生成的表上的left join,以显示缺失的日期为0 Price。

with minmax as (
      select id, min(report_date) as mindt, max(report_date)  as maxdt
      from t
      group by id
     )
select m.id, m.report_date, coalesce(t.price, 0) as price 
from (select *, 
      generate_series(date_trunc('MONTH',mindt+interval '1' day),
                      date_trunc('MONTH',maxdt+interval '1' day), 
                      interval '1' month) - interval '1 day' as report_date
      from minmax
     ) m 
left join t on m.report_date = t.report_date

Sample Demo

最新更新