sequenceMatch и sequenceCount: детекция последовательностей
windowFunnel отвечает на вопрос “до какого шага дошёл пользователь”. sequenceMatch отвечает на вопрос “произошла ли конкретная последовательность событий?” sequenceCount — “сколько раз произошла эта последовательность?” Оба используют гибкий паттерн-синтаксис с поддержкой временных условий.
sequenceMatch: бинарный детектор паттернов
sequenceMatch('pattern')(timestamp, cond1, cond2, ...)
Возвращает UInt8: 1 если паттерн найден, 0 если нет.
timestamp— DateTime или UInt32 (временная метка события)cond1, cond2, ...— логические выражения (условия), до 32 штук'pattern'— строка паттерна, описывающая нужную последовательность
Синтаксис паттернов
| Элемент | Значение |
|---|---|
(?1) | Условие 1 выполнено (первое условие в списке) |
(?2) | Условие 2 выполнено (второе условие в списке) |
.* | Любые события между (ноль или больше) |
(?t<=1800) | Не более 1800 секунд прошло с предыдущего совпавшего события |
(?t>=60) | Не менее 60 секунд прошло с предыдущего совпавшего события |
(?1).*(?2) | Условие 1, затем условие 2 с любым промежутком |
(?1)(?t<=1800)(?2) | Условие 1, затем условие 2 в пределах 30 минут |
(?1)(?2) | Условие 1, непосредственно за ним условие 2 |
Условия нумеруются от 1 до 32 в порядке их перечисления в вызове функции.
Примеры sequenceMatch
-- Пользователь просмотрел товар, а затем купил -- в любое время
SELECT
user_id,
sequenceMatch('(?1).*(?2)')(
ts,
event_type = 'view',
event_type = 'purchase'
) AS ever_converted
FROM events
GROUP BY user_id;
-- Пользователь просмотрел, а затем купил -- в пределах 30 минут
SELECT
user_id,
sequenceMatch('(?1)(?t<=1800)(?2)')(
ts,
event_type = 'view',
event_type = 'purchase'
) AS quick_convert
FROM events
GROUP BY user_id;
-- Сложный паттерн: поиск, затем просмотр, затем покупка в пределах часа
SELECT
user_id,
sequenceMatch('(?1).*(?2)(?t<=3600)(?3)')(
ts,
event_type = 'search',
event_type = 'view',
event_type = 'purchase'
) AS search_to_buy
FROM events
GROUP BY user_id;
sequenceCount: подсчёт непересекающихся цепочек
sequenceCount('pattern')(timestamp, cond1, cond2, ...)
Возвращает UInt64 — число непересекающихся вхождений паттерна.
-- Сколько раз пользователь делал поиск, а затем кликал на результат?
SELECT
user_id,
sequenceCount('(?1).*(?2)')(
ts,
event_type = 'search',
event_type = 'click'
) AS search_click_pairs
FROM events
GROUP BY user_id
ORDER BY search_click_pairs DESC
LIMIT 10;
“Непересекающиеся” означает: после нахождения паттерна поиск продолжается с позиции после последнего совпавшего события, а не с начала. Это предотвращает многократный счёт одной и той же цепочки.
Сравнение: sequenceMatch vs sequenceCount vs windowFunnel
| Функция | Что возвращает | Когда использовать |
|---|---|---|
windowFunnel | UInt8 — максимальный шаг воронки | Анализ конверсионной воронки (до какого шага дошёл) |
sequenceMatch | UInt8 (0 или 1) — было/не было | Детекция конкретного паттерна поведения |
sequenceCount | UInt64 — число вхождений | Подсчёт повторяющихся паттернов (сессий, попыток) |
-- Пример: сравнение трёх подходов на одних данных
SELECT
user_id,
windowFunnel(86400)(ts, event_type='view', event_type='cart', event_type='purchase') AS funnel_level,
sequenceMatch('(?1).*(?2)')(ts, event_type='view', event_type='purchase') AS ever_bought_after_view,
sequenceCount('(?1).*(?2)')(ts, event_type='search', event_type='purchase') AS search_to_buy_count
FROM events
GROUP BY user_id;
Ограничение: отрицательные условия
sequenceMatch не поддерживает прямое отрицание шагов. Нельзя написать “пользователь сделал A, не сделал B, потом сделал C” как единый паттерн.
-- Такого синтаксиса НЕ существует:
-- sequenceMatch('(?1)(?!2).*(?3)')(ts, cond1, cond2, cond3) -- ошибка
-- Обходной путь: разделить на два запроса
-- Запрос 1: найти пользователей с паттерном A -> C
-- Запрос 2: исключить тех, у кого было B между A и C
sequenceMatch не поддерживает отрицательные условия в паттерне. Для логики “A произошло, B не произошло, C произошло” используйте комбинацию sequenceMatch с HAVING или вложенными запросами. Прямого синтаксиса для отрицания шага нет.
Ключевые выводы
sequenceMatch('(?1).*(?2)')(ts, cond1, cond2)— детектор паттерна; возвращает 0 или 1.sequenceCount('(?1).*(?2)')(ts, cond1, cond2)— счётчик непересекающихся вхождений; возвращает UInt64.- Временные условия
(?t<=N)и(?t>=N)задают максимальный и минимальный интервал между событиями в секундах. - До 32 условий:
(?1)через(?32)— нумерация в порядке перечисления в вызове функции. - Отрицательные условия не поддерживаются — для таких сценариев нужны составные запросы.
- Выбор функции:
windowFunnel— для воронки (до какого шага),sequenceMatch— было/не было,sequenceCount— сколько раз.