#clickhouse
Вопрос:
Я хочу проводить аналитику хранения/событий на основе данных о скрытых точках, которые хранятся в ClickHouse. Допустим, у меня есть два типа событий: app_launch (buried_point_id=1) и user_register (buried_point_id=2). Я хочу понять: в течение 1-дневного временного окна, сколько пользователей регистрируется после запуска приложения. См. Примеры данных о погребенных точках ниже:
похоронен_поинт_ид | случилось _ат | идентификатор пользователя |
---|---|---|
1 | 1613923200 | 123 |
1 | 1614009600 | 345 |
2 | 1613966400 | 123 |
2 | 1614009600 | 234 |
2 | 1614182400 | 345 |
Вот запрос, который я хочу выполнить:
SELECT COUNT (DISTINCT t1.user_id), COUNT (DISTINCT t2.user_id)
FROM
(SELECT user_id,
happened_at
FROM buried_points
WHERE buried_point_id = 1
AND happened_at >= 1613923200
AND happened_at <= 1614182400
AND ) AS t1
ASOF LEFT JOIN
(SELECT user_id,
happened_at
FROM buried_points
WHERE buried_point_id = 2
AND happened_at >= 1613923200
AND happened_at <= 1614182400) AS t2
ON t1.user_id = t2.user_id
AND t1.happened_at < t2.happened_at
AND t2.happened_at - t1.happened_at < 86400;
Это ожидаемый результат запроса:
2 (123,345), 1 (123)
Однако, согласно документам ClickHouse, может поддерживаться только 1 неравенство:
Вы можете использовать любое количество условий равенства и ровно одно условие ближайшего совпадения. Например, ВЫБЕРИТЕ count() ИЗ table_1 В КАЧЕСТВЕ ЛЕВОГО СОЕДИНЕНИЯ table_2 В table_1.a == table_2.b И table_2.t <= table_1.t. Условия, поддерживаемые для ближайшего соответствия:>, < = table_1.t. Условия, поддерживаемые для ближайшего соответствия:>>=, < = table_1.t. Условия, поддерживаемые для ближайшего соответствия:>><,
Однако мне нужно 2 неравенства для выполнения моей работы — Есть ли решение этой проблемы?
Ответ №1:
Рассмотрите возможность использования специальной последовательности агрегатных функций:
SELECT
user_id,
sequenceMatch('(?1)(?2)')(happened_at, buried_point_id = 1, buried_point_id = 2) retention
FROM (
/* emulate test dataset */
SELECT data.1 buried_point_id, data.2 happened_at, data.3 user_id
FROM (
SELECT arrayJoin(
[(1, 1613966400, 123),
(1, 1613966411, 123),
(1, 1613966422, 123),
(1, 1614009600, 345),
(1, 1614009611, 345),
(2, 1613923200, 123),
(2, 1614009600, 234),
(2, 1614182400, 345)]) data)
)
WHERE happened_at >= 1613923200 AND happened_at <= 1614182400
GROUP BY user_id
/*
┌─user_id─┬─retention─┐
│ 123 │ 0 │
│ 234 │ 0 │
│ 345 │ 1 │
└─────────┴───────────┘
*/
Расчет на основе minIf:
SELECT
user_id,
minIf(happened_at, buried_point_id = 1) first_launch,
minIf(happened_at, buried_point_id = 2) first_registration,
first_launch != 0 and first_registration > first_launch ? 1 : 0 AS is_user_registered_after_launch
FROM (
/* emulate test dataset */
SELECT data.1 buried_point_id, data.2 happened_at, data.3 user_id
FROM (
SELECT arrayJoin(
[(1, 1613966400, 123),
(1, 1614009600, 345),
(2, 1613923200, 123),
(2, 1614009600, 234),
(2, 1614182400, 345)]) data)
)
WHERE happened_at >= 1613923200 AND happened_at <= 1614182400
GROUP BY user_id
/*
┌─user_id─┬─first_launch─┬─first_registration─┬─is_user_registered_after_launch─┐
│ 123 │ 1613966400 │ 1613923200 │ 0 │
│ 234 │ 0 │ 1614009600 │ 0 │
│ 345 │ 1614009600 │ 1614182400 │ 1 │
└─────────┴──────────────┴────────────────────┴─────────────────────────────────┘
*/
Комментарии:
1. Привет, Владимир, спасибо, что поделился потрясающей функцией! У меня есть еще один вопрос по этому поводу, из официального документа, похоже
retention
, нельзя определить соотношение времени как условия. В этом случае я хочу, чтобы регистрация пользователей происходила только после запуска приложения. Если я изменю тестовые случаи на: [(1, 1613966400, 123), (1, 1614009600, 345), (2, 1613923200, 123), (2, 1614009600, 234), (2, 1614182400, 345)] он все равно вернет return [1,1] для пользователя 123, знаете ли вы, как я могу определить соотношение времени в условиях? Спасибо!2. Привет @PangFish, посмотри на обновленный ответ.
3. Привет @владимир, спасибо за обновления, я многое узнал о агрегатных функциях в ClickHouse. Однако мне также нужно определить временное окно для второго события, скажем, через 3600 секунд после того, как произошло первое событие, я попробовал
sequenceMatch('(?1)(?t<=3600)(?2)')(happened_at, buried_point_id = 1, buried_point_id = 2) retention
, это сработало хорошо!4. Кроме того, у меня есть вопрос о функции
windowFunnel
, кажется, она идеально соответствует моему варианту использования, но я обнаружил, что в ней есть ошибка после запуска тестового набора данных. Это гарантируетtimestamp of cond2 <= timestamp of cond1 window
, но не гарантируетtimestamp of cond2 > timestamp of cond1
, поэтому остальная часть моего набора данных работает: (123, 1), (234, 0), (345, 1). Вы случайно не знаете об этой проблеме, есть ли способ ее решить?