I have a table like this:
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');
and I need to fill up the missing dates (imagine a time series) in this way:
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
where the time range goes from '2014-06-01' to '2015-04-01'.
What I did so far is:
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)
The returned values of the query do not fill up the whole time range (until 2015-04). How can I make the query work in that way?
UPDATE
I think I have fixed it:
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;
Best Answer
First it generates a series of date by label and then you can use either a lateral join:
or a subquery:
dbfiddle here