Это довольно просто, если вы используете диапазоны дат.
CREATE TABLE SUBSCRIPTION (
date date,
product text,
period interval,
subscriber_id int,
units int
);
INSERT INTO SUBSCRIPTION VALUES
('2019-07-03', 'A' , '1 month', 1, 1),
('2019-07-02', 'A', '1 year', 2, 1),
('2019-07-01', 'B', '1 year', 1, 1),
('2019-06-30', 'B', '1 month', 3, 1),
('2019-06-30', 'A', '1 month', 4, 1),
('2019-06-03', 'B', '1 month', 4, 1),
('2019-06-03', 'A', '1 month', 1, 1);
-- First, get the list of dateranges, from 2019-06-03 to 2019-07-03 (or whatever you want)
WITH dates as (
SELECT daterange(t::date, (t + interval '1' day)::date, '[)')
FROM generate_series('2019-06-03'::timestamp without time zone,
'2019-07-03',
interval '1' day) as g(t)
)
SELECT lower(daterange)::date, count(distinct subscriber_id)
FROM dates
LEFT JOIN subscription ON daterange <@
daterange(subscription.date,
(subscription.date + period)::date)
GROUP BY daterange
;
lower | count
------------+-------
2019-06-03 | 2
2019-06-04 | 2
2019-06-05 | 2
2019-06-06 | 2
2019-06-07 | 2
2019-06-08 | 2
2019-06-09 | 2
2019-06-10 | 2
2019-06-11 | 2
2019-06-12 | 2
2019-06-13 | 2
2019-06-14 | 2
2019-06-15 | 2
2019-06-16 | 2
2019-06-17 | 2
2019-06-18 | 2
2019-06-19 | 2
2019-06-20 | 2
2019-06-21 | 2
2019-06-22 | 2
2019-06-23 | 2
2019-06-24 | 2
2019-06-25 | 2
2019-06-26 | 2
2019-06-27 | 2
2019-06-28 | 2
2019-06-29 | 2
2019-06-30 | 3
2019-07-01 | 3
2019-07-02 | 4
2019-07-03 | 4
(31 rows)
Вы можете повысить производительность, сохранив (и индексировав) допустимое время подписки в виде диапазона дат вместо его вычисления в запросе.
РЕДАКТИРОВАТЬ: Как отметил Джей, я забыл группировать по продукту:
WITH dates as (
SELECT daterange(t::date, (t + interval '1' day)::date, '[)')
FROM generate_series('2019-06-03'::timestamp without time zone,
'2019-07-03',
interval '1' day) as g(t)
)
SELECT lower(daterange)::date, product, count(distinct subscriber_id)
FROM dates
LEFT JOIN subscription ON daterange <@
daterange(subscription.date,
(subscription.date + period)::date)
GROUP BY daterange, product
;
lower | product | count
------------+---------+-------
2019-06-03 | A | 1
2019-06-03 | B | 1
2019-06-04 | A | 1
2019-06-04 | B | 1
2019-06-05 | A | 1
2019-06-05 | B | 1
2019-06-06 | A | 1
2019-06-06 | B | 1
2019-06-07 | A | 1
2019-06-07 | B | 1
2019-06-08 | A | 1
2019-06-08 | B | 1
2019-06-09 | A | 1
2019-06-09 | B | 1
2019-06-10 | A | 1
2019-06-10 | B | 1
2019-06-11 | A | 1
2019-06-11 | B | 1
2019-06-12 | A | 1
2019-06-12 | B | 1
2019-06-13 | A | 1
2019-06-13 | B | 1
2019-06-14 | A | 1
2019-06-14 | B | 1
2019-06-15 | A | 1
2019-06-15 | B | 1
2019-06-16 | A | 1
2019-06-16 | B | 1
2019-06-17 | A | 1
2019-06-17 | B | 1
2019-06-18 | A | 1
2019-06-18 | B | 1
2019-06-19 | A | 1
2019-06-19 | B | 1
2019-06-20 | A | 1
2019-06-20 | B | 1
2019-06-21 | A | 1
2019-06-21 | B | 1
2019-06-22 | A | 1
2019-06-22 | B | 1
2019-06-23 | A | 1
2019-06-23 | B | 1
2019-06-24 | A | 1
2019-06-24 | B | 1
2019-06-25 | A | 1
2019-06-25 | B | 1
2019-06-26 | A | 1
2019-06-26 | B | 1
2019-06-27 | A | 1
2019-06-27 | B | 1
2019-06-28 | A | 1
2019-06-28 | B | 1
2019-06-29 | A | 1
2019-06-29 | B | 1
2019-06-30 | A | 2
2019-06-30 | B | 2
2019-07-01 | A | 2
2019-07-01 | B | 3
2019-07-02 | A | 3
2019-07-02 | B | 3
2019-07-03 | A | 3
2019-07-03 | B | 2