Сложная оконная функция

Дается файл в формате csv.

client_id timestamp
8225501 1722747654
8225501 1722747683
9609244 1722747693
... ...

Надо вывести за каждый день количество таких client_id, которые встречались за последние 30 дней не менее 5 раз. Должны также учитываться client_id, не присутствующие в данный день. Вопрос: можно ли создать свою функцию для окна (фрейма), наподобие приведенной ниже?

SET @func = (SELECT SUM(COUNT(*)) OVER()
            FROM w
            GROUP BY id
            HAVING COUNT(*) >= 5);

WITH a AS (
            SELECT   timestamp / 24 / 3600   AS day,
                     client_id               AS id
            FROM dataset)

SELECT DISTINCT(day),
       @func OVER w                          AS cnt
FROM a
WINDOW w AS (ORDER BY day
             RANGE BETWEEN 30 PRECEDING AND CURRENT ROW)

Ответы (2 шт):

Автор решения: pegoopik

Смутно описано ТЗ, но попробуйте так:

SELECT day, id
FROM(
  SELECT day, id,
    COUNT(*)OVER(PARTITION BY id ORDER BY day RANGE 30 PRECEDING AND CURRENT ROW)Cnt_Client
  FROM(
    SELECT 
      client_id AS id,
      timestamp / 24 / 3600   AS day
    FROM dataset
  )T
)T
WHERE Cnt_Client >= 5
GROUP BY day, id
→ Ссылка
Автор решения: ValNik

Пример

  1. Сперва группируем по дням и клиентам - сколько раз клиент был за этот день
  2. Вычисляем с помощью оконной функции, сколько раз клиент был за последние 30 дней, считая от даты в текущей строке. Окно range between interval 30 day preceding and current row
  3. Для каждой даты вычисляем число клиентов в этот день, у которых количество присутствия в предыдущие 30 дней >=5

Ваши данные для пример расширил для показа суммирования за день и 30 предыдущих дней.

select dt,sum(case when cntClientLast30day>=5 then 1 else 0 end) cntClientsGe5time30day
from(
   select *
       ,sum(cntClientDay)over(partition by client_id order by dt
           range between interval 30 day preceding  and current row ) cntClientLast30day
   from(
      select client_id,cast(from_unixtime(timestamp) as date) dt
        ,count(*) cntClientDay
      from test
      group by client_id,cast(from_unixtime(timestamp) as date)
    )a
)b
group by dt
dt cntClientsGt5time30day
2024-08-04 2
2024-08-09 1
2024-08-10 1
2024-08-11 1
2024-08-12 1
2024-08-27 1
2024-09-08 1
2024-09-19 0
2024-10-01 0
select *
    ,sum(cntClientDay)over(partition by client_id order by dt
      range between interval 1 month preceding  and current row ) cntClientLast30day
from(
select client_id,cast(from_unixtime(timestamp) as date) dt
  ,count(*) cntClientDay
from test
group by client_id,cast(from_unixtime(timestamp) as date)
)a
order by dt,client_id
client_id dt cntClientDay cntClientLast30day
8225501 2024-08-04 5 5
9609244 2024-08-04 6 6
8225501 2024-08-09 1 6
8225501 2024-08-10 1 7
8225501 2024-08-11 1 8
8225501 2024-08-12 1 9
8225501 2024-08-27 1 10
8225501 2024-09-08 1 6
9609244 2024-09-08 1 1
8225501 2024-09-19 1 3
8225501 2024-10-01 1 3
select client_id,cast(from_unixtime(timestamp) as date) dt
  ,count(*) cntClientDay
from test
group by client_id,cast(from_unixtime(timestamp) as date)
client_id dt cntClientDay
8225501 2024-08-04 5
9609244 2024-08-04 6
9609244 2024-09-08 1
8225501 2024-08-09 1
8225501 2024-08-10 1
8225501 2024-08-11 1
8225501 2024-08-12 1
8225501 2024-08-27 1
8225501 2024-09-08 1
8225501 2024-09-19 1
8225501 2024-10-01 1

Данные для примера

select * ,from_unixtime(timestamp) dttm
from test;
client_id timestamp dttm
8225501 1722747654 2024-08-04 05:00:54
8225501 1722747683 2024-08-04 05:01:23
9609244 1722747693 2024-08-04 05:01:33
9609244 1722757693 2024-08-04 07:48:13
9609244 1722767693 2024-08-04 10:34:53
9609244 1722777693 2024-08-04 13:21:33
9609244 1722787693 2024-08-04 16:08:13
9609244 1722797693 2024-08-04 18:54:53
9609244 1725797693 2024-09-08 12:14:53
8225501 1722757683 2024-08-04 07:48:03
8225501 1722767683 2024-08-04 10:34:43
8225501 1722777683 2024-08-04 13:21:23
8225501 1723177683 2024-08-09 04:28:03
8225501 1723277683 2024-08-10 08:14:43
8225501 1723377683 2024-08-11 12:01:23
8225501 1723477683 2024-08-12 15:48:03
8225501 1724777683 2024-08-27 16:54:43
8225501 1725777683 2024-09-08 06:41:23
8225501 1726777683 2024-09-19 20:28:03
8225501 1727777683 2024-10-01 10:14:43

Если же нужно вычислять окно (30 дней) от данной строки с учетом времени, п1. и п.2 нужно переставить.

fiddle

→ Ссылка