Уникальные совокупные клиенты по каждому дню

#sql #hive #count #hiveql #window-functions

#sql #улей #количество #hiveql #окно-функции

Вопрос:

Задача: получить общее количество уникальных кумулятивных клиентов по каждой причине снижения и по каждому дню.

     Input data sample:
     --------- -------------- ------------ ------ 
    | Cust_Id |  Decline_Dt  |  Reason    | Days |
     --------- -------------- ------------ ------ 
    |   A     |  08-09-2020  |  Reason_1  |   0  |
    |   A     |  08-09-2020  |  Reason_1  |   1  | 
    |   A     |  08-09-2020  |  Reason_1  |   2  |
    |   A     |  08-09-2020  |  Reason_1  |   4  |
    |   B     |  08-09-2020  |  Reason_1  |   0  |
    |   B     |  08-09-2020  |  Reason_1  |   2  |
    |   B     |  08-09-2020  |  Reason_1  |   3  |
    |   C     |  08-09-2020  |  Reason_1  |   1  |
     --------- -------------- ------------ ------    
1) Decline_dt - The date on which the payment was declined. (Ignore it for this task)
2) Days - Indicates the # of days after the payment decline happened, the customer interacted with IVR channel. 
3) Reason - Indicates the payment decline reason 
    
    --Expected Output:
     --------------- ----------- --------------- ---------------------------- 
    |   Reason      |   Days    | Unique_mtns   | total_cumulative_customers |
     --------------- ----------- --------------- ---------------------------- 
    |   Reason_1    |   0       |   2           |           2                |              
    |   Reason_1    |   1       |   2           |           3                |
    |   Reason_1    |   2       |   2           |           3                | 
    |   Reason_1    |   3       |   1           |           3                | 
    |   Reason_1    |   4       |   1           |           3                | 
     ------------------------------------------------------------------------ 
 

Мой запрос улья:

 select a.Reason
        , a.days
        -- , count(distinct a.cust_id) as unique_mtns
        , count(distinct a.cust_id) over (partition by Reason 
                                                order by a.days rows between unbounded preceding and current row) 
                                        as total_cumulative_customers
from table as a  
group by a.reason
        , a.days
 

Вывод (неверный):

  --------------- ----------- ---------------------------- 
|   Reason      |   Days    | total_cumulative_customers |
 --------------- ----------- ---------------------------- 
|   Reason_1    |   0       |               2            |              
|   Reason_1    |   1       |               2            |
|   Reason_1    |   2       |               2            | 
|   Reason_1    |   3       |               1            | 
|   Reason_1    |   4       |               1            | 
 -------------------------------------------------------- 
 

В идеале я бы ожидал, что оконная функция будет выполняться без группирования.
Однако я получаю сообщение об ошибке без группирования. Когда я использую group by, я не получаю кумулятивных клиентов.

Ответ №1:

Если я правильно вас понял, вы можете использовать подзапрос для вычисления первого дня для каждого кортежа клиентов / причин, а затем выполнить условную агрегацию:

 select reason, days, 
    count(distinct cust_id) as unique_mtns,
    sum(sum(case when days = min_days then 1 else 0 end)) 
        over(partition by reason order by days) as total_cumulative_customers
from (
    select reason, cust_id, 
        min(days) over(partition by reason, cust_id) as min_days
    from mytable
) t
group by reason, days
 

Комментарии:

1. Некоторые столбцы недоступны во внешнем запросе. Также min_days отсутствует в group by, поэтому должен быть другой sum , или он должен выдавать ошибку (может быть, в Hive это работает по-другому?). Это можно исправить, отметив первый день во внутреннем запросе с помощью lag(0, 1, 1) over(partition by reason, cust_id order by days asc)

2. Спасибо @GMB! Ваше решение отлично работает после внесенного вами редактирования путем добавления другой суммы. Спасибо @astentx, добавив еще одну сумму, я запустил запрос.

Ответ №2:

Я бы рекомендовал использовать row_number() для перечисления строк или данного клиента и причины. Ваш код использует count(distinct) идентификатор пользователя, предполагая, что у вас могут быть дубликаты в данный день.

Это было бы:

 select reason, days, count(distinct cust_id) as unique_mtns,
       sum(sum(case when seqnum = 1 then 1 else 0 end)) over (partition by reason order by days) as total_cumulative_customers
from (select t.*,
             row_number() over (partition by reason, cust_id order by days) as seqnum
      from t
     ) t
group by reason, days
order by reason, days;
 

Комментарии:

1. У меня нет дубликатов в данный день. count(*) дает мне тот же результат. В любом случае, ваше решение также работает. Спасибо!

2. @Ashish . , , Ваш вопрос использует count(distinct) , создавая впечатление, что ваши данные имеют такие дубликаты.