Как сгруппировать по каждой дате с определенным условием в postgresql? - PullRequest
0 голосов
/ 03 июля 2019

У меня есть такая таблица в postgresql. Каждый ряд показывает, что клиент подписался на нашу продукцию. Например, Клиент 1 оплатил подписку на 1 месяц в 2019-07-03.

date       product period subscriber_id units
2019-07-03       A 1Month             1     1
2019-07-02       A  1Year             2     1
2019-07-01       B  1Year             1     1
2019-06-30       B 1Month             3     1
2019-06-30       A 1Month             4     1
2019-06-03       B 1Month             4     1
2019-06-03       A 1Month             1     1

Я хочу рассчитать общее количество действительных разных подписчиков на каждый день, результат будет выглядеть как

base_date product total_distinct_count 
2019-07-03      A                    3
2019-07-03      B                    3
2019-07-02      A                    3
2019-07-02      B                    3
2019-07-01      A                    2
2019-07-01      B                    3
2019-06-30      A                    2
2019-06-30      B                    1

...

Есть 3 разных клиента (1, 2, 4), которые все еще подписывают продукт А на 2019-07-03 в первом ряду. Я пытался использовать каждый день и ,

SELECT date, COUNT(DISTINCT(subscribers_id))
-- do some conditions
GROUP BY date, product

Я не знаю, как группировать с этим условием. Если есть лучший способ решить эту проблему. Я буду очень признателен !!!

1 Ответ

1 голос
/ 05 июля 2019

Это довольно просто, если вы используете диапазоны дат.

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
...