Перейти к основному содержимому
Перейти к основному содержимому

Параметрические агрегатные функции

Некоторые агрегатные функции могут принимать не только аргументные колонки (используемые для сжатия), но и набор параметров – константы для инициализации. Синтаксис включает две пары скобок вместо одной. Первая пара предназначена для параметров, а вторая – для аргументов.

histogram

Вычисляет адаптивный гистограмму. Она не гарантирует точные результаты.

histogram(number_of_bins)(values)

Функция использует Адаптивный алгоритм потокового параллельного дерева решений. Границы корзин гистограммы корректируются по мере поступления новых данных в функцию. В общем случае ширины корзин не равны.

Аргументы

valuesВыражение, приводящее к входным значениям.

Параметры

number_of_bins — Верхний предел для количества корзин в гистограмме. Функция автоматически вычисляет количество корзин. Она пытается достичь указанного числа корзин, но если это не удается, использует меньшее количество корзин.

Возвращаемые значения

[(lower_1, upper_1, height_1), ... (lower_N, upper_N, height_N)]
  • lower — Нижняя граница корзины.
  • upper — Верхняя граница корзины.
  • height — Вычисленная высота корзины.

Пример

SELECT histogram(5)(number + 1)
FROM (
    SELECT *
    FROM system.numbers
    LIMIT 20
)
┌─histogram(5)(plus(number, 1))───────────────────────────────────────────┐
│ [(1,4.5,4),(4.5,8.5,4),(8.5,12.75,4.125),(12.75,17,4.625),(17,20,3.25)] │
└─────────────────────────────────────────────────────────────────────────┘

Вы можете визуализировать гистограмму с помощью функции bar, например:

WITH histogram(5)(rand() % 100) AS hist
SELECT
    arrayJoin(hist).3 AS height,
    bar(height, 0, 6, 5) AS bar
FROM
(
    SELECT *
    FROM system.numbers
    LIMIT 20
)
┌─height─┬─bar───┐
│  2.125 │ █▋    │
│   3.25 │ ██▌   │
│  5.625 │ ████▏ │
│  5.625 │ ████▏ │
│  3.375 │ ██▌   │
└────────┴───────┘

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

sequenceMatch

Проверяет, содержит ли последовательность цепочку событий, которая соответствует шаблону.

Синтаксис

sequenceMatch(pattern)(timestamp, cond1, cond2, ...)
примечание

События, происходящие в одну и ту же секунду, могут находиться в последовательности в неопределенном порядке, что влияет на результат.

Аргументы

  • timestamp — Колонка, считающаяся содержащей временные данные. Типичные типы данных: Date и DateTime. Вы также можете использовать любой из поддерживаемых типов UInt.

  • cond1, cond2 — Условия, описывающие цепочку событий. Тип данных: UInt8. Вы можете передать до 32 аргументов условий. Функция принимает во внимание только события, описанные в этих условиях. Если последовательность содержит данные, которые не описаны в условии, функция пропускает их.

Параметры

Возвращаемые значения

  • 1, если шаблон совпал.
  • 0, если шаблон не совпал.

Тип: UInt8.

Синтаксис шаблона

  • (?N) — Соответствует аргументу условия на позиции N. Условия нумеруются в диапазоне [1, 32]. Например, (?1) соответствует аргументу, переданному в параметр cond1.

  • .* — Соответствует любому количеству событий. Вам не нужны аргументы условий для соответствия этому элементу шаблона.

  • (?t operator value) — Устанавливает время в секундах, которое должно разделять два события. Например, шаблон (?1)(?t>1800)(?2) соответствует событиям, которые происходят более чем через 1800 секунд друг от друга. Произвольное количество любых событий может находиться между этими событиями. Вы можете использовать операторы >=, >, <, <=, ==.

Примеры

Рассмотрим данные в таблице t:

┌─time─┬─number─┐
│    1 │      1 │
│    2 │      3 │
│    3 │      2 │
└──────┴────────┘

Выполните запрос:

SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2) FROM t
┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2))─┐
│                                                                     1 │
└───────────────────────────────────────────────────────────────────────┘

Функция нашла цепочку событий, где номер 2 следует за номером 1. Она пропустила номер 3 между ними, потому что этот номер не описан как событие. Если мы хотим учитывать этот номер при поиске цепочки событий, приведенной в примере, мы должны создать для него условие.

SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 3) FROM t
┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2), equals(number, 3))─┐
│                                                                                        0 │
└──────────────────────────────────────────────────────────────────────────────────────────┘

В этом случае функция не смогла найти цепочку событий, соответствующую шаблону, потому что событие для номера 3 произошло между 1 и 2. Если в том же случае мы проверили условие для номера 4, последовательность соответствовала бы шаблону.

SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 4) FROM t
┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2), equals(number, 4))─┐
│                                                                                        1 │
└──────────────────────────────────────────────────────────────────────────────────────────┘

Смотрите также

sequenceCount

Считывает количество цепочек событий, которые соответствовали шаблону. Функция ищет цепочки событий, которые не перекрываются. Она начинает искать следующую цепочку после того, как текущая цепочка найдена.

примечание

События, происходящие в одну и ту же секунду, могут находиться в последовательности в неопределенном порядке, что влияет на результат.

Синтаксис

sequenceCount(pattern)(timestamp, cond1, cond2, ...)

Аргументы

  • timestamp — Колонка, считающаяся содержащей временные данные. Типичные типы данных: Date и DateTime. Вы также можете использовать любой из поддерживаемых типов UInt.

  • cond1, cond2 — Условия, описывающие цепочку событий. Тип данных: UInt8. Вы можете передать до 32 аргументов условий. Функция принимает во внимание только события, описанные в этих условиях. Если последовательность содержит данные, которые не описаны в условии, функция пропускает их.

Параметры

Возвращаемые значения

  • Количество несовпадающих цепочек событий, которые соответствуют шаблону.

Тип: UInt64.

Пример

Рассмотрим данные в таблице t:

┌─time─┬─number─┐
│    1 │      1 │
│    2 │      3 │
│    3 │      2 │
│    4 │      1 │
│    5 │      3 │
│    6 │      2 │
└──────┴────────┘

Подсчитайте, сколько раз номер 2 происходит после номера 1 с любым количеством других чисел между ними:

SELECT sequenceCount('(?1).*(?2)')(time, number = 1, number = 2) FROM t
┌─sequenceCount('(?1).*(?2)')(time, equals(number, 1), equals(number, 2))─┐
│                                                                       2 │
└─────────────────────────────────────────────────────────────────────────┘

sequenceMatchEvents

Возвращает временные метки событий самых длинных цепочек событий, которые соответствуют шаблону.

примечание

События, происходящие в одну и ту же секунду, могут находиться в последовательности в неопределенном порядке, что влияет на результат.

Синтаксис

sequenceMatchEvents(pattern)(timestamp, cond1, cond2, ...)

Аргументы

  • timestamp — Колонка, считающаяся содержащей временные данные. Типичные типы данных: Date и DateTime. Вы также можете использовать любой из поддерживаемых типов UInt.

  • cond1, cond2 — Условия, описывающие цепочку событий. Тип данных: UInt8. Вы можете передать до 32 аргументов условий. Функция принимает во внимание только события, описанные в этих условиях. Если последовательность содержит данные, которые не описаны в условии, функция пропускает их.

Параметры

Возвращаемые значения

  • Массив временных меток для соответствующих аргументов условий (?N) из цепочки событий. Позиция в массиве соответствует позиции аргумента условия в шаблоне.

Тип: Массив.

Пример

Рассмотрим данные в таблице t:

┌─time─┬─number─┐
│    1 │      1 │
│    2 │      3 │
│    3 │      2 │
│    4 │      1 │
│    5 │      3 │
│    6 │      2 │
└──────┴────────┘

Верните временные метки событий для самой длинной цепочки

SELECT sequenceMatchEvents('(?1).*(?2).*(?1)(?3)')(time, number = 1, number = 2, number = 4) FROM t
┌─sequenceMatchEvents('(?1).*(?2).*(?1)(?3)')(time, equals(number, 1), equals(number, 2), equals(number, 4))─┐
│ [1,3,4]                                                                                                    │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Смотрите также

windowFunnel

Ищет цепочки событий в скользящем временном окне и вычисляет максимальное количество событий, которые произошли из цепочки.

Функция работает по следующему алгоритму:

  • Функция ищет данные, которые запускают первое условие в цепочке, и устанавливает счетчик событий на 1. Это момент, когда начинает действовать скользящее окно.

  • Если события из цепочки происходят последовательно в окне, счетчик увеличивается. Если последовательность событий нарушается, счетчик не увеличивается.

  • Если в данных имеется несколько цепочек событий на разных точках завершения, функция выведет только размер самой длинной цепочки.

Синтаксис

windowFunnel(window, [mode, [mode, ... ]])(timestamp, cond1, cond2, ..., condN)

Аргументы

  • timestamp — Название колонки, содержащей временную метку. Поддерживаемые типы данных: Date, DateTime и другие беззнаковые целые типы (обратите внимание, что, хотя временная метка поддерживает тип UInt64, ее значение не может превышать максимум Int64, который равен 2^63 - 1).
  • cond — Условия или данные, описывающие цепочку событий. UInt8.

Параметры

  • window — Длина скользящего окна, это временной интервал между первым и последним условием. Единица window зависит от самого timestamp и варьируется. Определяется выражением timestamp of cond1 <= timestamp of cond2 <= ... <= timestamp of condN <= timestamp of cond1 + window.
  • mode — Это необязательный аргумент. Можно установить один или несколько режимов.
    • 'strict_deduplication' — Если одно и то же условие выполняется для последовательности событий, то такое повторяющееся событие прерывает дальнейшую обработку. Примечание: это может работать непредсказуемо, если для одного и того же события выполняется несколько условий.
    • 'strict_order' — Не допускайте вмешательства других событий. Например, в случае A->B->D->C, остановите поиск A->B->C на D, и максимальный уровень событий равен 2.
    • 'strict_increase' — Применяйте условия только к событиям со строго возрастающими временными метками.
    • 'strict_once' — Считайте каждое событие только один раз в цепочке, даже если оно соответствует условию несколько раз.

Возвращаемое значение

Максимальное количество последовательных срабатывающих условий из цепочки в пределах скользящего временного окна. Все цепочки в выборке анализируются.

Тип: Integer.

Пример

Определите, достаточно ли заданного периода времени для пользователя, чтобы выбрать телефон и дважды купить его в интернет-магазине.

Установите следующую цепочку событий:

  1. Пользователь вошел в свой аккаунт в магазине (eventID = 1003).
  2. Пользователь ищет телефон (eventID = 1007, product = 'phone').
  3. Пользователь оформил заказ (eventID = 1009).
  4. Пользователь оформил заказ еще раз (eventID = 1010).

Входная таблица:

┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
│ 2019-01-28 │       1 │ 2019-01-29 10:00:00 │    1003 │ phone   │
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
│ 2019-01-31 │       1 │ 2019-01-31 09:00:00 │    1007 │ phone   │
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
│ 2019-01-30 │       1 │ 2019-01-30 08:00:00 │    1009 │ phone   │
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘
┌─event_date─┬─user_id─┬───────────timestamp─┬─eventID─┬─product─┐
│ 2019-02-01 │       1 │ 2019-02-01 08:00:00 │    1010 │ phone   │
└────────────┴─────────┴─────────────────────┴─────────┴─────────┘

Узнайте, как далеко пользователь user_id смог пройти по цепочке в период с января по февраль 2019 года.

Запрос:

SELECT
    level,
    count() AS c
FROM
(
    SELECT
        user_id,
        windowFunnel(6048000000000000)(timestamp, eventID = 1003, eventID = 1009, eventID = 1007, eventID = 1010) AS level
    FROM trend
    WHERE (event_date >= '2019-01-01') AND (event_date <= '2019-02-02')
    GROUP BY user_id
)
GROUP BY level
ORDER BY level ASC;

Результат:

┌─level─┬─c─┐
│     4 │ 1 │
└───────┴───┘

retention

Функция принимает в качестве аргументов набор условий от 1 до 32 аргументов типа UInt8, которые указывают, было ли выполнено определенное условие для события. Любое условие может быть указано в качестве аргумента (как в WHERE).

Условия, кроме первого, применяются парами: результат второго будет истинен, если первое и второе истинны, третьего — если первое и третье истинны и т.д.

Синтаксис

retention(cond1, cond2, ..., cond32);

Аргументы

  • cond — Выражение, возвращающее результат UInt8 (1 или 0).

Возвращаемое значение

Массив из 1 или 0.

  • 1 — Условие было выполнено для события.
  • 0 — Условие не было выполнено для события.

Тип: UInt8.

Пример

Рассмотрим пример расчета функции retention для определения посещаемости сайта.

1. Создайте таблицу, чтобы проиллюстрировать пример.

CREATE TABLE retention_test(date Date, uid Int32) ENGINE = Memory;

INSERT INTO retention_test SELECT '2020-01-01', number FROM numbers(5);
INSERT INTO retention_test SELECT '2020-01-02', number FROM numbers(10);
INSERT INTO retention_test SELECT '2020-01-03', number FROM numbers(15);

Входная таблица:

Запрос:

SELECT * FROM retention_test

Результат:

┌───────date─┬─uid─┐
│ 2020-01-01 │   0 │
│ 2020-01-01 │   1 │
│ 2020-01-01 │   2 │
│ 2020-01-01 │   3 │
│ 2020-01-01 │   4 │
└────────────┴─────┘
┌───────date─┬─uid─┐
│ 2020-01-02 │   0 │
│ 2020-01-02 │   1 │
│ 2020-01-02 │   2 │
│ 2020-01-02 │   3 │
│ 2020-01-02 │   4 │
│ 2020-01-02 │   5 │
│ 2020-01-02 │   6 │
│ 2020-01-02 │   7 │
│ 2020-01-02 │   8 │
│ 2020-01-02 │   9 │
└────────────┴─────┘
┌───────date─┬─uid─┐
│ 2020-01-03 │   0 │
│ 2020-01-03 │   1 │
│ 2020-01-03 │   2 │
│ 2020-01-03 │   3 │
│ 2020-01-03 │   4 │
│ 2020-01-03 │   5 │
│ 2020-01-03 │   6 │
│ 2020-01-03 │   7 │
│ 2020-01-03 │   8 │
│ 2020-01-03 │   9 │
│ 2020-01-03 │  10 │
│ 2020-01-03 │  11 │
│ 2020-01-03 │  12 │
│ 2020-01-03 │  13 │
│ 2020-01-03 │  14 │
└────────────┴─────┘

2. Группируйте пользователей по уникальному ID uid, используя функцию retention.

Запрос:

SELECT
    uid,
    retention(date = '2020-01-01', date = '2020-01-02', date = '2020-01-03') AS r
FROM retention_test
WHERE date IN ('2020-01-01', '2020-01-02', '2020-01-03')
GROUP BY uid
ORDER BY uid ASC

Результат:

┌─uid─┬─r───────┐
│   0 │ [1,1,1] │
│   1 │ [1,1,1] │
│   2 │ [1,1,1] │
│   3 │ [1,1,1] │
│   4 │ [1,1,1] │
│   5 │ [0,0,0] │
│   6 │ [0,0,0] │
│   7 │ [0,0,0] │
│   8 │ [0,0,0] │
│   9 │ [0,0,0] │
│  10 │ [0,0,0] │
│  11 │ [0,0,0] │
│  12 │ [0,0,0] │
│  13 │ [0,0,0] │
│  14 │ [0,0,0] │
└─────┴─────────┘

3. Подсчитайте общее количество посещений сайта в день.

Запрос:

SELECT
    sum(r[1]) AS r1,
    sum(r[2]) AS r2,
    sum(r[3]) AS r3
FROM
(
    SELECT
        uid,
        retention(date = '2020-01-01', date = '2020-01-02', date = '2020-01-03') AS r
    FROM retention_test
    WHERE date IN ('2020-01-01', '2020-01-02', '2020-01-03')
    GROUP BY uid
)

Результат:

┌─r1─┬─r2─┬─r3─┐
│  5 │  5 │  5 │
└────┴────┴────┘

Где:

  • r1 — количество уникальных посетителей, которые посетили сайт с 2020-01-01 (условие cond1).
  • r2 — количество уникальных посетителей, которые посетили сайт в течение конкретного времени между 2020-01-01 и 2020-01-02 (условия cond1 и cond2).
  • r3 — количество уникальных посетителей, которые посетили сайт в течение конкретного времени 2020-01-01 и 2020-01-03 (условия cond1 и cond3).

uniqUpTo(N)(x)

Вычисляет количество различных значений аргумента до указанного предела N. Если количество различных значений аргумента больше N, эта функция возвращает N + 1, в противном случае вычисляет точное значение.

Рекомендуется использовать с небольшими N, до 10. Максимальное значение N равно 100.

Для состояния агрегатной функции эта функция использует объем памяти, равный 1 + N * размер одного значения в байтах. При работе со строками эта функция хранит некриптографический хеш размером 8 байт; вычисление для строк аппроксимировано.

Например, если у вас есть таблица, которая регистрирует каждый поисковый запрос, сделанный пользователями на вашем веб-сайте. Каждая строка в таблице представляет собой отдельный поисковый запрос с колонками для идентификатора пользователя, поискового запроса и временной метки запроса. Вы можете использовать uniqUpTo, чтобы сгенерировать отчет, показывающий только ключевые слова, которые использовались как минимум 5 уникальными пользователями.

SELECT SearchPhrase
FROM SearchLog
GROUP BY SearchPhrase
HAVING uniqUpTo(4)(UserID) >= 5

uniqUpTo(4)(UserID) вычисляет количество уникальных значений UserID для каждого SearchPhrase, но учитывает только до 4 уникальных значений. Если более 4 уникальных значений UserID для SearchPhrase, функция возвращает 5 (4 + 1). Затем выражение HAVING фильтрует значения SearchPhrase, для которых количество уникальных значений UserID меньше 5. Это даст вам список поисковых ключевых слов, которые использовались как минимум 5 уникальными пользователями.

sumMapFiltered

Эта функция работает так же, как sumMap, за исключением того, что она также принимает массив ключей для фильтрации в качестве параметра. Это может быть особенно полезно при работе с высокой кардинальностью ключей.

Синтаксис

sumMapFiltered(keys_to_keep)(keys, values)

Параметры

Возвращаемое значение

  • Возвращает кортеж из двух массивов: ключи в отсортированном порядке и значения, суммированные для соответствующих ключей.

Пример

Запрос:

CREATE TABLE sum_map
(
    `date` Date,
    `timeslot` DateTime,
    `statusMap` Nested(status UInt16, requests UInt64)
)
ENGINE = Log

INSERT INTO sum_map VALUES
    ('2000-01-01', '2000-01-01 00:00:00', [1, 2, 3], [10, 10, 10]),
    ('2000-01-01', '2000-01-01 00:00:00', [3, 4, 5], [10, 10, 10]),
    ('2000-01-01', '2000-01-01 00:01:00', [4, 5, 6], [10, 10, 10]),
    ('2000-01-01', '2000-01-01 00:01:00', [6, 7, 8], [10, 10, 10]);
SELECT sumMapFiltered([1, 4, 8])(statusMap.status, statusMap.requests) FROM sum_map;

Результат:

   ┌─sumMapFiltered([1, 4, 8])(statusMap.status, statusMap.requests)─┐
1. │ ([1,4,8],[10,20,10])                                            │
   └─────────────────────────────────────────────────────────────────┘

sumMapFilteredWithOverflow

Эта функция работает так же, как sumMap, за исключением того, что она также принимает массив ключей для фильтрации в качестве параметра. Это может быть особенно полезно при работе с высокой кардинальностью ключей. Она отличается от функции sumMapFiltered тем, что выполняет суммирование с переполнением – т.е. возвращает тот же тип данных для суммирования, что и тип данных аргумента.

Синтаксис

sumMapFilteredWithOverflow(keys_to_keep)(keys, values)

Параметры

Возвращаемое значение

  • Возвращает кортеж из двух массивов: ключи в отсортированном порядке и значения, суммированные для соответствующих ключей.

Пример

В этом примере мы создаем таблицу sum_map, вставляем в нее некоторые данные, а затем используем как sumMapFilteredWithOverflow, так и sumMapFiltered и функцию toTypeName для сравнения результатов. Где requests был типа UInt8 в созданной таблице, sumMapFiltered повысил тип суммируемых значений до UInt64, чтобы избежать переполнения, в то время как sumMapFilteredWithOverflow сохранил тип в UInt8, который недостаточно велик, чтобы хранить результат – т.е. произошло переполнение.

Запрос:

CREATE TABLE sum_map
(
    `date` Date,
    `timeslot` DateTime,
    `statusMap` Nested(status UInt8, requests UInt8)
)
ENGINE = Log

INSERT INTO sum_map VALUES
    ('2000-01-01', '2000-01-01 00:00:00', [1, 2, 3], [10, 10, 10]),
    ('2000-01-01', '2000-01-01 00:00:00', [3, 4, 5], [10, 10, 10]),
    ('2000-01-01', '2000-01-01 00:01:00', [4, 5, 6], [10, 10, 10]),
    ('2000-01-01', '2000-01-01 00:01:00', [6, 7, 8], [10, 10, 10]);
SELECT sumMapFilteredWithOverflow([1, 4, 8])(statusMap.status, statusMap.requests) as summap_overflow, toTypeName(summap_overflow) FROM sum_map;
SELECT sumMapFiltered([1, 4, 8])(statusMap.status, statusMap.requests) as summap, toTypeName(summap) FROM sum_map;

Результат:

   ┌─sum──────────────────┬─toTypeName(sum)───────────────────┐
1. │ ([1,4,8],[10,20,10]) │ Tuple(Array(UInt8), Array(UInt8)) │
   └──────────────────────┴───────────────────────────────────┘
   ┌─summap───────────────┬─toTypeName(summap)─────────────────┐
1. │ ([1,4,8],[10,20,10]) │ Tuple(Array(UInt8), Array(UInt64)) │
   └──────────────────────┴────────────────────────────────────┘

sequenceNextNode

Возвращает значение следующего события, которое соответствовало цепочке событий.

Экспериментальная функция, SET allow_experimental_funnel_functions = 1, чтобы включить ее.

Синтаксис

sequenceNextNode(direction, base)(timestamp, event_column, base_condition, event1, event2, event3, ...)

Параметры

  • direction — Используется для навигации по направлениям.

    • forward — Движение вперед.
    • backward — Движение назад.
  • base — Используется для установки базовой точки.

    • head — Установите базовую точку на первое событие.
    • tail — Установите базовую точку на последнее событие.
    • first_match — Установите базовую точку на первое соответствующее event1.
    • last_match — Установите базовую точку на последнее соответствующее event1.

Аргументы

  • timestamp — Название колонки, содержащей временную метку. Поддерживаемые типы данных: Date, DateTime и другие беззнаковые целые типы.
  • event_column — Название колонки, содержащей значение следующего события, которое необходимо вернуть. Поддерживаемые типы данных: String и Nullable(String).
  • base_condition — Условие, которое должна выполнять базовая точка.
  • event1, event2, ... — Условия, описывающие цепочку событий. UInt8.

Возвращаемые значения

  • event_column[next_index] — Если шаблон совпадает и существует следующее значение.
  • NULL - Если шаблон не совпадает или следующее значение не существует.

Тип: Nullable(String).

Пример

Это можно использовать, когда события являются A->B->C->D->E, и вы хотите знать событие, следующее за B->C, которое является D.

Запрос для поиска события, следущего за A->B:

CREATE TABLE test_flow (
    dt DateTime,
    id int,
    page String)
ENGINE = MergeTree()
PARTITION BY toYYYYMMDD(dt)
ORDER BY id;

INSERT INTO test_flow VALUES (1, 1, 'A') (2, 1, 'B') (3, 1, 'C') (4, 1, 'D') (5, 1, 'E');

SELECT id, sequenceNextNode('forward', 'head')(dt, page, page = 'A', page = 'A', page = 'B') as next_flow FROM test_flow GROUP BY id;

Результат:

┌─id─┬─next_flow─┐
│  1 │ C         │
└────┴───────────┘

Поведение для forward и head

ALTER TABLE test_flow DELETE WHERE 1 = 1 settings mutations_sync = 1;

INSERT INTO test_flow VALUES (1, 1, 'Home') (2, 1, 'Gift') (3, 1, 'Exit');
INSERT INTO test_flow VALUES (1, 2, 'Home') (2, 2, 'Home') (3, 2, 'Gift') (4, 2, 'Basket');
INSERT INTO test_flow VALUES (1, 3, 'Gift') (2, 3, 'Home') (3, 3, 'Gift') (4, 3, 'Basket');
SELECT id, sequenceNextNode('forward', 'head')(dt, page, page = 'Home', page = 'Home', page = 'Gift') FROM test_flow GROUP BY id;

                  dt   id   page
 1970-01-01 09:00:01    1   Home // Base point, Matched with Home
 1970-01-01 09:00:02    1   Gift // Matched with Gift
 1970-01-01 09:00:03    1   Exit // The result

 1970-01-01 09:00:01    2   Home // Base point, Matched with Home
 1970-01-01 09:00:02    2   Home // Unmatched with Gift
 1970-01-01 09:00:03    2   Gift
 1970-01-01 09:00:04    2   Basket

 1970-01-01 09:00:01    3   Gift // Base point, Unmatched with Home
 1970-01-01 09:00:02    3   Home
 1970-01-01 09:00:03    3   Gift
 1970-01-01 09:00:04    3   Basket

Поведение для backward и tail

SELECT id, sequenceNextNode('backward', 'tail')(dt, page, page = 'Basket', page = 'Basket', page = 'Gift') FROM test_flow GROUP BY id;

                 dt   id   page
1970-01-01 09:00:01    1   Home
1970-01-01 09:00:02    1   Gift
1970-01-01 09:00:03    1   Exit // Base point, Unmatched with Basket

1970-01-01 09:00:01    2   Home
1970-01-01 09:00:02    2   Home // The result
1970-01-01 09:00:03    2   Gift // Matched with Gift
1970-01-01 09:00:04    2   Basket // Base point, Matched with Basket

1970-01-01 09:00:01    3   Gift
1970-01-01 09:00:02    3   Home // The result
1970-01-01 09:00:03    3   Gift // Base point, Matched with Gift
1970-01-01 09:00:04    3   Basket // Base point, Matched with Basket

Поведение для forward и first_match

SELECT id, sequenceNextNode('forward', 'first_match')(dt, page, page = 'Gift', page = 'Gift') FROM test_flow GROUP BY id;

                 dt   id   page
1970-01-01 09:00:01    1   Home
1970-01-01 09:00:02    1   Gift // Base point
1970-01-01 09:00:03    1   Exit // The result

1970-01-01 09:00:01    2   Home
1970-01-01 09:00:02    2   Home
1970-01-01 09:00:03    2   Gift // Base point
1970-01-01 09:00:04    2   Basket  The result

1970-01-01 09:00:01    3   Gift // Base point
1970-01-01 09:00:02    3   Home // The result
1970-01-01 09:00:03    3   Gift
1970-01-01 09:00:04    3   Basket
SELECT id, sequenceNextNode('forward', 'first_match')(dt, page, page = 'Gift', page = 'Gift', page = 'Home') FROM test_flow GROUP BY id;

                 dt   id   page
1970-01-01 09:00:01    1   Home
1970-01-01 09:00:02    1   Gift // Base point
1970-01-01 09:00:03    1   Exit // Unmatched with Home

1970-01-01 09:00:01    2   Home
1970-01-01 09:00:02    2   Home
1970-01-01 09:00:03    2   Gift // Base point
1970-01-01 09:00:04    2   Basket // Unmatched with Home

1970-01-01 09:00:01    3   Gift // Base point
1970-01-01 09:00:02    3   Home // Matched with Home
1970-01-01 09:00:03    3   Gift // The result
1970-01-01 09:00:04    3   Basket

Поведение для backward и last_match

SELECT id, sequenceNextNode('backward', 'last_match')(dt, page, page = 'Gift', page = 'Gift') FROM test_flow GROUP BY id;

                 dt   id   page
1970-01-01 09:00:01    1   Home // The result
1970-01-01 09:00:02    1   Gift // Base point
1970-01-01 09:00:03    1   Exit

1970-01-01 09:00:01    2   Home
1970-01-01 09:00:02    2   Home // The result
1970-01-01 09:00:03    2   Gift // Base point
1970-01-01 09:00:04    2   Basket

1970-01-01 09:00:01    3   Gift
1970-01-01 09:00:02    3   Home // The result
1970-01-01 09:00:03    3   Gift // Base point
1970-01-01 09:00:04    3   Basket
SELECT id, sequenceNextNode('backward', 'last_match')(dt, page, page = 'Gift', page = 'Gift', page = 'Home') FROM test_flow GROUP BY id;

                 dt   id   page
1970-01-01 09:00:01    1   Home // Matched with Home, the result is null
1970-01-01 09:00:02    1   Gift // Base point
1970-01-01 09:00:03    1   Exit

1970-01-01 09:00:01    2   Home // The result
1970-01-01 09:00:02    2   Home // Matched with Home
1970-01-01 09:00:03    2   Gift // Base point
1970-01-01 09:00:04    2   Basket

1970-01-01 09:00:01    3   Gift // The result
1970-01-01 09:00:02    3   Home // Matched with Home
1970-01-01 09:00:03    3   Gift // Base point
1970-01-01 09:00:04    3   Basket

Поведение для base_condition

CREATE TABLE test_flow_basecond
(
    `dt` DateTime,
    `id` int,
    `page` String,
    `ref` String
)
ENGINE = MergeTree
PARTITION BY toYYYYMMDD(dt)
ORDER BY id;

INSERT INTO test_flow_basecond VALUES (1, 1, 'A', 'ref4') (2, 1, 'A', 'ref3') (3, 1, 'B', 'ref2') (4, 1, 'B', 'ref1');
SELECT id, sequenceNextNode('forward', 'head')(dt, page, ref = 'ref1', page = 'A') FROM test_flow_basecond GROUP BY id;

                  dt   id   page   ref
 1970-01-01 09:00:01    1   A      ref4 // The head can not be base point because the ref column of the head unmatched with 'ref1'.
 1970-01-01 09:00:02    1   A      ref3
 1970-01-01 09:00:03    1   B      ref2
 1970-01-01 09:00:04    1   B      ref1
SELECT id, sequenceNextNode('backward', 'tail')(dt, page, ref = 'ref4', page = 'B') FROM test_flow_basecond GROUP BY id;

                  dt   id   page   ref
 1970-01-01 09:00:01    1   A      ref4
 1970-01-01 09:00:02    1   A      ref3
 1970-01-01 09:00:03    1   B      ref2
 1970-01-01 09:00:04    1   B      ref1 // The tail can not be base point because the ref column of the tail unmatched with 'ref4'.
SELECT id, sequenceNextNode('forward', 'first_match')(dt, page, ref = 'ref3', page = 'A') FROM test_flow_basecond GROUP BY id;

                  dt   id   page   ref
 1970-01-01 09:00:01    1   A      ref4 // This row can not be base point because the ref column unmatched with 'ref3'.
 1970-01-01 09:00:02    1   A      ref3 // Base point
 1970-01-01 09:00:03    1   B      ref2 // The result
 1970-01-01 09:00:04    1   B      ref1
SELECT id, sequenceNextNode('backward', 'last_match')(dt, page, ref = 'ref2', page = 'B') FROM test_flow_basecond GROUP BY id;

                  dt   id   page   ref
 1970-01-01 09:00:01    1   A      ref4
 1970-01-01 09:00:02    1   A      ref3 // The result
 1970-01-01 09:00:03    1   B      ref2 // Base point
 1970-01-01 09:00:04    1   B      ref1 // This row can not be base point because the ref column unmatched with 'ref2'.