【问题标题】:Add Missing monthly dates in a timeseries data in Postgresql在 Postgresql 的时间序列数据中添加缺少的每月日期
【发布时间】:2016-12-01 04:00:58
【问题描述】:

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

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 效率并不高。

谢谢

【问题讨论】:

    标签: sql postgresql window-functions sql-date-functions


    【解决方案1】:

    您可以使用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只是生成样本数据。

    【讨论】:

      【解决方案2】:

      这比 Gordon 的查询略有改进,在某些情况下无法获取一个月的最后日期。

      基本上,您会在此生成的表格上为每个 ID(使用 generate_series)和 left join 生成 minmax 日期之间的所有月末日期,以显示价格为 0 的缺失日期。

      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

      【讨论】:

        猜你喜欢
        • 2020-09-20
        • 2021-08-11
        • 2020-05-07
        • 2014-04-30
        • 2018-12-06
        • 1970-01-01
        • 2019-09-14
        • 2015-08-08
        相关资源
        最近更新 更多