SQL - Инкрементное изложение дела - Когортный анализ - PullRequest
1 голос
/ 03 августа 2020

Моя основная цель - получить быстрое представление о среднем доходе для пользователей за период 30, 90, 180, 180+ дней. У меня есть электронное письмо, дата их присоединения к определенной группе и дата получения дохода

create temporary table cohorts (
  email varchar(64)
, start_date timestamp
, purchase_date timestamp
, amount decimal(10,2)
)
;

insert into cohorts 
values 
  ('johnsmith@domain.com', '2020-01-01 00:00:00', '2020-01-01 12:00:00', '200.00')
, ('happyday@domain.com', '2020-01-01 00:00:00', '2020-02-28 00:00:00','100.00')
, ('happyday@domain.com', '2020-01-01 00:00:00', '2020-01-28 00:00:00','100.00')
, ('susieq@domain.com', '2020-01-01 00:00:00', '2020-05-01 00:00:00', '50.00')
, ('janedoe@domain.com', '2020-01-01 00:00:00', '2020-03-30 00:00:00', '75.00')
, ('janedoe@domain.com', '2020-01-01 00:00:00', '2020-07-30 00:00:00', '75.00')
;

Если бы я хотел увидеть средний доход пользователей за определенный период времени, я бы написал что-то вроде:

select 
case 
    when datediff(day,start_date, purchase_date) < 30 then 'Within 30' 
    when datediff(day,start_date, purchase_date) < 90 then 'Within 90' 
    when datediff(day,start_date, purchase_date) < 180 then 'Within 180' 
    else 'Older than 180' 
end as cohort_flag 
, count(distinct email) num_of_emails 
, sum(amount) summed_amt 
, sum(amount)/count(distinct email) as avg_value 
from cohorts
group by 1 

  cohort_flag  num_of_emails  summed_amt  avg_value
     Within 30            2       300.0      150.0
     Within 90            2       175.0       87.5
    Within 180            1        50.0       50.0
Older than 180            1        75.0       75.0

Однако, поскольку оператор case преобразуется в первое истинное предложение, он не включает доход от более ранних «когорт». Мой желаемый результат будет ниже, где пользователи в более ранних когортах являются частью других:

  cohort_flag  num_of_emails  summed_amt  avg_value
     Within 30            2       300.0      150.0
     Within 90            3       475.0      158.33
    Within 180            4       525.0      131.25
Older than 180            4       600.0      150.0

1 Ответ

1 голос
/ 03 августа 2020

Вы должны использовать одну и ту же строку таблицы для более чем 1 группы, поэтому вам нужен такой запрос:

select 30 days_dif, 'Within 30' cohort_flag union all
select 90, 'Within 90' union all
select 180, 'Within 180' union all
select 2147483647, 'Older than 180'

(я надеюсь, что синтаксис поддерживается Redshift) который определяет группы, а затем LEFT присоединяет их к таблице:

select t.cohort_flag, 
       count(distinct c.email) num_of_emails,
       coalesce(sum(c.amount), 0) summed_amt, 
       coalesce(sum(c.amount), 0) / nullif(count(distinct c.email), 0) as avg_value 
from (
  select 30 days_dif, 'Within 30' cohort_flag union all
  select 90, 'Within 90' union all
  select 180, 'Within 180' union all
  select 2147483647, 'Older than 180'
) t left join cohorts c
on datediff(day, c.start_date, c.purchase_date) < t.days_dif
group by t.days_dif, t.cohort_flag
order by t.days_dif
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...