使用上个月的值填充缺失的日期

Ran*_*ize 5 postgresql

我有一个这样的表:

CREATE TABLE my_data (label text, value integer, date date);
INSERT INTO my_data (label, value, date) VALUES 
('AAA', 10, '2014-06-01'),
('AAA', 30, '2014-09-01'),
('AAA', 40, '2014-10-01'),
('AAA', 50, '2015-02-01'),
('BBB', 20, '2014-11-01'),
('BBB', 10, '2015-02-01'),
('BBB', 70, '2015-04-01');
Run Code Online (Sandbox Code Playgroud)

我需要以这种方式填写缺失的日期(想象一个时间序列):

label | value | date
------+-------+------------
AAA   | 10    | 2014-06-01
AAA   | 10    | 2014-07-01
AAA   | 10    | 2014-08-01
AAA   | 30    | 2014-09-01
AAA   | 40    | 2014-10-01
AAA   | 40    | 2014-11-01
AAA   | 40    | 2014-12-01
AAA   | 40    | 2015-01-01
AAA   | 50    | 2015-02-01
AAA   | 50    | 2015-03-01
AAA   | 50    | 2015-04-01
BBB   | 20    | 2014-11-01
BBB   | 20    | 2014-12-01
BBB   | 20    | 2015-01-01
BBB   | 10    | 2015-02-01
BBB   | 10    | 2015-03-01
BBB   | 70    | 2015-04-01
Run Code Online (Sandbox Code Playgroud)

其中时间范围从“2014-06-01”到“2015-04-01”。到目前为止我所做的是:

WITH 
md AS
(
SELECT *, LEAD(date) OVER (PARTITION BY label ORDER BY date) AS next_date FROM my_data
),
calendar AS
(
select date::date from generate_series('2014-06-01'::date, '2015-04-01'::date, '1 month'::interval) date
)
SELECT m.label, m.value, c.date
FROM calendar c
JOIN md m
    ON c.date BETWEEN m.date AND (m.next_date - interval '1 month') order by label, date;
 label | value |    date    
-------+-------+------------
 AAA   |    10 | 2014-06-01
 AAA   |    10 | 2014-07-01
 AAA   |    10 | 2014-08-01
 AAA   |    30 | 2014-09-01
 AAA   |    40 | 2014-10-01
 AAA   |    40 | 2014-11-01
 AAA   |    40 | 2014-12-01
 AAA   |    40 | 2015-01-01
 BBB   |    20 | 2014-11-01
 BBB   |    20 | 2014-12-01
 BBB   |    20 | 2015-01-01
 BBB   |    10 | 2015-02-01
 BBB   |    10 | 2015-03-01
(13 rows)
Run Code Online (Sandbox Code Playgroud)

查询的返回值并未填满整个时间范围(直到 2015 年 4 月)。我怎样才能使查询以这种方式工作?

更新

我想我已经修复了它:

WITH 
md AS
(
SELECT *, LEAD(date) OVER (PARTITION BY label ORDER BY date) AS next_date FROM my_data
),
calendar AS
(
select date::date from generate_series('2014-06-01'::date, '2015-04-01'::date, '1 month'::interval) date
)
SELECT m.label, m.value, c.date
FROM calendar c
JOIN md m
    ON c.date BETWEEN m.date AND 
    (CASE WHEN m.next_date IS NULL THEN date '2015-04-01' ELSE m.next_date - interval '1 month' END) order by label, date;
Run Code Online (Sandbox Code Playgroud)

McN*_*ets 4

首先,它按标签生成一系列日期,然后您可以使用横向连接:

with a as
(
    select   label, generate_series(min(date), '2015-04-01'::date, interval '1 month') dt
    from     my_data
    group by label
)
select    label,
          t1.value,
           dt
from      a
left join lateral (select t1.value
                   from   my_data t1
                   where  t1.label = a.label
                   and    t1.date <= a.dt
                   order by label, date desc
                   limit 1) t1 on true
order by  label, dt
Run Code Online (Sandbox Code Playgroud)

或子查询:

with a as
(
    select   label, generate_series(min(date), '2015-04-01'::date, interval '1 month') dt
    from     my_data
    group by label
)
select    label,
          (select t1.value
           from   my_data t1
           where  t1.label = a.label
           and    t1.date <= a.dt
           order by label, date desc
           limit 1),
           dt
from      a
order by  label, dt
Run Code Online (Sandbox Code Playgroud)
标签| 价值| dt                    
:---- | ----: | :----------------------
AAA | 10 | 10 2014-06-01 00:00:00+01
AAA | 10 | 10 2014-07-01 00:00:00+01
AAA | 10 | 10 2014-08-01 00:00:00+01
AAA | 30| 2014-09-01 00:00:00+01
AAA | 40 | 40 2014-10-01 00:00:00+01
AAA | 40 | 40 2014-11-01 00:00:00+00
AAA | 40 | 40 2014-12-01 00:00:00+00
AAA | 40 | 40 2015-01-01 00:00:00+00
AAA | 50 | 50 2015-02-01 00:00:00+00
AAA | 50 | 50 2015-03-01 00:00:00+00
AAA | 50 | 50 2015-04-01 00:00:00+01
BBB | 20 | 2014-11-01 00:00:00+00
BBB | 20 | 2014-12-01 00:00:00+00
BBB | 20 | 2015-01-01 00:00:00+00
BBB | 10 | 10 2015-02-01 00:00:00+00
BBB | 10 | 10 2015-03-01 00:00:00+00
BBB | 70 | 70 2015-04-01 00:00:00+01

dbfiddle在这里