--- slug: /ru/sql-reference/aggregate-functions/parametric-functions sidebar_position: 38 sidebar_label: "Параметрические агрегатные функции" --- # Параметрические агрегатные функции {#aggregate_functions_parametric} Некоторые агрегатные функции могут принимать не только столбцы-аргументы (по которым производится свёртка), но и набор параметров - констант для инициализации. Синтаксис - две пары круглых скобок вместо одной. Первая - для параметров, вторая - для аргументов. ## histogram {#histogram} Рассчитывает адаптивную гистограмму. Не гарантирует точного результата. ``` sql histogram(number_of_bins)(values) ``` Функция использует [A Streaming Parallel Decision Tree Algorithm](http://jmlr.org/papers/volume11/ben-haim10a/ben-haim10a.pdf). Границы столбцов устанавливаются по мере поступления новых данных в функцию. В общем случае столбцы имею разную ширину. **Аргументы** `values` — [выражение](../syntax.md#syntax-expressions), предоставляющее входные значения. **Параметры** `number_of_bins` — максимальное количество корзин в гистограмме. Функция автоматически вычисляет количество корзин. Она пытается получить указанное количество корзин, но если не получилось, то в результате корзин будет меньше. **Возвращаемые значения** - [Массив](../../sql-reference/data-types/array.md) [кортежей](../../sql-reference/data-types/tuple.md) следующего вида: ``` [(lower_1, upper_1, height_1), ... (lower_N, upper_N, height_N)] ``` - `lower` — нижняя граница корзины. - `upper` — верхняя граница корзины. - `height` — количество значений в корзине. **Пример** ``` sql SELECT histogram(5)(number + 1) FROM ( SELECT * FROM system.numbers LIMIT 20 ) ``` ``` text ┌─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](../../sql-reference/aggregate-functions/parametric-functions.md#function-bar) можно визуализировать гистограмму, например: ``` sql 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 ) ``` ``` text ┌─height─┬─bar───┐ │ 2.125 │ █▋ │ │ 3.25 │ ██▌ │ │ 5.625 │ ████▏ │ │ 5.625 │ ████▏ │ │ 3.375 │ ██▌ │ └────────┴───────┘ ``` В этом случае необходимо помнить, что границы корзин гистограммы не известны. ## sequenceMatch(pattern)(timestamp, cond1, cond2, …) {#function-sequencematch} Проверяет, содержит ли последовательность событий цепочку, которая соответствует указанному шаблону. ``` sql sequenceMatch(pattern)(timestamp, cond1, cond2, ...) ``` :::danger "Предупреждение" События, произошедшие в одну и ту же секунду, располагаются в последовательности в неопределенном порядке, что может повлиять на результат работы функции. ::: **Аргументы** - `timestamp` — столбец, содержащий метки времени. Типичный тип данных столбца — `Date` или `DateTime`. Также можно использовать любой из поддержанных типов данных [UInt](../../sql-reference/aggregate-functions/parametric-functions.md). - `cond1`, `cond2` — условия, описывающие цепочку событий. Тип данных — `UInt8`. Можно использовать до 32 условий. Функция учитывает только те события, которые указаны в условиях. Функция пропускает данные из последовательности, если они не описаны ни в одном из условий. **Параметры** - `pattern` — строка с шаблоном. Смотрите [Синтаксис шаблонов](#sequence-function-pattern-syntax). **Возвращаемые значения** - 1, если цепочка событий, соответствующая шаблону найдена. - 0, если цепочка событий, соответствующая шаблону не найдена. Тип: `UInt8`. **Синтаксис шаблонов** - `(?N)` — соответствует условию на позиции `N`. Условия пронумерованы по порядку в диапазоне `[1, 32]`. Например, `(?1)` соответствует условию, заданному параметром `cond1`. - `.*` — соответствует любому количеству событий. Для этого элемента шаблона не надо задавать условия. - `(?t operator value)` — устанавливает время в секундах, которое должно разделять два события. Например, шаблон `(?1)(?t>1800)(?2)` соответствует событиям, которые произошли более чем через 1800 секунд друг от друга. Между этими событиями может находиться произвольное количество любых событий. Операторы могут быть `>=`, `>`, `<`, `<=`, `==`. **Примеры** Пусть таблица `t` содержит следующие данные: ``` text ┌─time─┬─number─┐ │ 1 │ 1 │ │ 2 │ 3 │ │ 3 │ 2 │ └──────┴────────┘ ``` Выполним запрос: ``` sql SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2) FROM t ``` ``` text ┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2))─┐ │ 1 │ └───────────────────────────────────────────────────────────────────────┘ ``` Функция нашла цепочку событий, в которой число 2 следует за числом 1. Число 3 между ними было пропущено, поскольку оно не было использовано ни в одном из условий. ``` sql SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 3) FROM t ``` ``` text ┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2), equals(number, 3))─┐ │ 0 │ └──────────────────────────────────────────────────────────────────────────────────────────┘ ``` В этом случае функция не может найти цепочку событий, соответствующую шаблону, поскольку событие для числа 3 произошло между 1 и 2. Если бы в этом же случае мы бы проверяли условие на событие для числа 4, то цепочка бы соответствовала шаблону. ``` sql SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 4) FROM t ``` ``` text ┌─sequenceMatch('(?1)(?2)')(time, equals(number, 1), equals(number, 2), equals(number, 4))─┐ │ 1 │ └──────────────────────────────────────────────────────────────────────────────────────────┘ ``` **Смотрите также** - [sequenceCount](#function-sequencecount) ## sequenceCount(pattern)(time, cond1, cond2, …) {#function-sequencecount} Вычисляет количество цепочек событий, соответствующих шаблону. Функция обнаруживает только непересекающиеся цепочки событий. Она начинает искать следующую цепочку только после того, как полностью совпала текущая цепочка событий. :::danger "Предупреждение" События, произошедшие в одну и ту же секунду, располагаются в последовательности в неопределенном порядке, что может повлиять на результат работы функции. ::: ``` sql sequenceCount(pattern)(timestamp, cond1, cond2, ...) ``` **Аргументы** - `timestamp` — столбец, содержащий метки времени. Типичный тип данных столбца — `Date` или `DateTime`. Также можно использовать любой из поддержанных типов данных [UInt](../../sql-reference/aggregate-functions/parametric-functions.md). - `cond1`, `cond2` — условия, описывающие цепочку событий. Тип данных — `UInt8`. Можно использовать до 32 условий. Функция учитывает только те события, которые указаны в условиях. Функция пропускает данные из последовательности, если они не описаны ни в одном из условий. **Параметры** - `pattern` — строка с шаблоном. Смотрите [Синтаксис шаблонов](#sequence-function-pattern-syntax). **Возвращаемое значение** - Число непересекающихся цепочек событий, соответствущих шаблону. Тип: `UInt64`. **Пример** Пусть таблица `t` содержит следующие данные: ``` text ┌─time─┬─number─┐ │ 1 │ 1 │ │ 2 │ 3 │ │ 3 │ 2 │ │ 4 │ 1 │ │ 5 │ 3 │ │ 6 │ 2 │ └──────┴────────┘ ``` Вычислим сколько раз число 2 стоит после числа 1, причем между 1 и 2 могут быть любые числа: ``` sql SELECT sequenceCount('(?1).*(?2)')(time, number = 1, number = 2) FROM t ``` ``` text ┌─sequenceCount('(?1).*(?2)')(time, equals(number, 1), equals(number, 2))─┐ │ 2 │ └─────────────────────────────────────────────────────────────────────────┘ ``` **Смотрите также** - [sequenceMatch](#function-sequencematch) ## windowFunnel {#windowfunnel} Отыскивает цепочки событий в скользящем окне по времени и вычисляет максимальное количество произошедших событий из цепочки. Функция работает по алгоритму: - Функция отыскивает данные, на которых срабатывает первое условие из цепочки, и присваивает счетчику событий значение 1. С этого же момента начинается отсчет времени скользящего окна. - Если в пределах окна последовательно попадаются события из цепочки, то счетчик увеличивается. Если последовательность событий нарушается, то счетчик не растет. - Если в данных оказалось несколько цепочек разной степени завершенности, то функция выдаст только размер самой длинной цепочки. **Синтаксис** ``` sql windowFunnel(window, [mode, [mode, ... ]])(timestamp, cond1, cond2, ..., condN) ``` **Аргументы** - `timestamp` — имя столбца, содержащего временные отметки. [Date](../../sql-reference/aggregate-functions/parametric-functions.md), [DateTime](../../sql-reference/aggregate-functions/parametric-functions.md#data_type-datetime) и другие параметры с типом `Integer`. В случае хранения меток времени в столбцах с типом `UInt64`, максимально допустимое значение соответствует ограничению для типа `Int64`, т.е. равно `2^63-1`. - `cond` — условия или данные, описывающие цепочку событий. [UInt8](../../sql-reference/aggregate-functions/parametric-functions.md). **Параметры** - `window` — ширина скользящего окна по времени. Это время между первым и последним условием. Единица измерения зависит от `timestamp` и может варьироваться. Должно соблюдаться условие `timestamp события cond1 <= timestamp события cond2 <= ... <= timestamp события condN <= timestamp события cond1 + window`. - `mode` — необязательный параметр. Может быть установленно несколько значений одновременно. - `'strict'` — не учитывать подряд идущие повторяющиеся события. - `'strict_order'` — запрещает посторонние события в искомой последовательности. Например, при поиске цепочки `A->B->C` в `A->B->D->C` поиск будет остановлен на `D` и функция вернет 2. - `'strict_increase'` — условия прменяются только для событий со строго возрастающими временными метками. **Возвращаемое значение** Максимальное количество последовательно сработавших условий из цепочки в пределах скользящего окна по времени. Исследуются все цепочки в выборке. Тип: `Integer`. **Пример** Определим, успевает ли пользователь за установленный период выбрать телефон в интернет-магазине, купить его и сделать повторный заказ. Зададим следующую цепочку событий: 1. Пользователь вошел в личный кабинет (`eventID = 1001`). 2. Пользователь ищет телефон (`eventID = 1003, product = 'phone'`). 3. Пользователь сделал заказ (`eventID = 1009`) 4. Пользователь сделал повторный заказ (`eventID = 1010`). Входная таблица: ``` text ┌─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-го года. Запрос: ``` sql 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; ``` ## retention {#retention} Аналитическая функция, которая показывает, насколько выдерживаются те или иные условия, например, удержание динамики/уровня [посещаемости сайта](https://yandex.ru/support/partner2/statistics/metrika-visitors-statistics.html?lang=ru). Функция принимает набор (от 1 до 32) логических условий, как в [WHERE](../../sql-reference/statements/select/where.md#select-where), и применяет их к заданному набору данных. Условия, кроме первого, применяются попарно: результат второго будет истинным, если истинно первое и второе, третьего - если истинно первое и третье и т.д. **Синтаксис** ``` sql retention(cond1, cond2, ..., cond32) ``` **Аргументы** - `cond` — вычисляемое условие или выражение, которое возвращает `UInt8` результат (1/0). **Возвращаемое значение** Массив из 1 или 0. - 1 — условие выполнено. - 0 — условие не выполнено. Тип: `UInt8`. **Пример** Рассмотрим пример расчета функции `retention` для определения посещаемости сайта. **1.** Создадим таблицу для илюстрации примера. ``` sql 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); ``` Входная таблица: Запрос: ``` sql SELECT * FROM retention_test ``` Ответ: ``` text ┌───────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.** Сгруппируем пользователей по уникальному идентификатору `uid` с помощью функции `retention`. Запрос: ``` sql 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 ``` Результат: ``` text ┌─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.** Рассчитаем количество посещений сайта за день. Запрос: ``` sql 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 ) ``` Результат: ``` text ┌─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) {#uniquptonx} Вычисляет количество различных значений аргумента, если оно меньше или равно N. В случае, если количество различных значений аргумента больше N, возвращает N + 1. Рекомендуется использовать для маленьких N - до 10. Максимальное значение N - 100. Для состояния агрегатной функции используется количество оперативки равное 1 + N \* размер одного значения байт. Для строк запоминается не криптографический хэш, имеющий размер 8 байт. То есть, для строк вычисление приближённое. Функция также работает для нескольких аргументов. Работает максимально быстро за исключением патологических случаев, когда используется большое значение N и количество уникальных значений чуть меньше N. Пример применения: ``` text Задача: показывать в отчёте только поисковые фразы, по которым было хотя бы 5 уникальных посетителей. Решение: пишем в запросе GROUP BY SearchPhrase HAVING uniqUpTo(4)(UserID) >= 5 ``` ## sequenceNextNode {#sequenceNextNode} Возвращает значение следующего события, соответствующего цепочке событий. _Экспериментальная функция, чтобы включить ее, выполните: `SET allow_experimental_funnel_functions = 1`._ **Синтаксис** ``` sql sequenceNextNode(direction, base)(timestamp, event_column, base_condition, event1, event2, event3, ...) ``` **Параметры** - `direction` — используется для навигации по направлениям. - forward — двигаться вперед. - backward — двигаться назад. - `base` — используется для задания начальной точки. - head — установить начальную точку на первое событие цепочки. - tail — установить начальную точку на последнее событие цепочки. - first_match — установить начальную точку на первое соответствующее событие `event1`. - last_match — установить начальную точку на последнее соответствующее событие `event1`. **Аргументы** - `timestamp` — название столбца, содержащего `timestamp`. Поддерживаемые типы данных: [Date](../../sql-reference/data-types/date.md), [DateTime](../../sql-reference/data-types/datetime.md#data_type-datetime) и другие беззнаковые целые типы. - `event_column` — название столбца, содержащего значение следующего возвращаемого события. Поддерживаемые типы данных: [String](../../sql-reference/data-types/string.md) и [Nullable(String)](../../sql-reference/data-types/nullable.md). - `base_condition` — условие, которому должна соответствовать исходная точка. - `event1`, `event2`, ... — условия, описывающие цепочку событий. [UInt8](../../sql-reference/data-types/int-uint.md). **Возвращаемые значения** - `event_column[next_index]` — если есть совпадение с шаблоном и существует следующее значение. - `NULL` — если нет совпадений с шаблоном или следующего значения не существует. Тип: [Nullable(String)](../../sql-reference/data-types/nullable.md). **Пример** Функцию можно использовать, если есть цепочка событий A->B->C->D->E, и вы хотите определить событие, следующее за B->C, то есть D. Запрос ищет событие после A->B: ``` sql 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; ``` Результат: ``` text ┌─id─┬─next_flow─┐ │ 1 │ C │ └────┴───────────┘ ``` **Поведение для `forward` и `head`** ``` sql 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'); ``` ``` sql 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 // Исходная точка, совпадение с Home 1970-01-01 09:00:02 1 Gift // Совпадение с Gift 1970-01-01 09:00:03 1 Exit // Результат 1970-01-01 09:00:01 2 Home // Исходная точка, совпадение с Home 1970-01-01 09:00:02 2 Home // Несовпадение с 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 // Исходная точка, несовпадение с 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`** ``` sql 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 // Исходная точка, несовпадение с Basket 1970-01-01 09:00:01 2 Home 1970-01-01 09:00:02 2 Home // Результат 1970-01-01 09:00:03 2 Gift // Совпадение с Gift 1970-01-01 09:00:04 2 Basket // Исходная точка, совпадение с Basket 1970-01-01 09:00:01 3 Gift 1970-01-01 09:00:02 3 Home // Результат 1970-01-01 09:00:03 3 Gift // Исходная точка, совпадение с Gift 1970-01-01 09:00:04 3 Basket // Исходная точка, совпадение с Basket ``` **Поведение для `forward` и `first_match`** ``` sql 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 // Исходная точка 1970-01-01 09:00:03 1 Exit // Результат 1970-01-01 09:00:01 2 Home 1970-01-01 09:00:02 2 Home 1970-01-01 09:00:03 2 Gift // Исходная точка 1970-01-01 09:00:04 2 Basket Результат 1970-01-01 09:00:01 3 Gift // Исходная точка 1970-01-01 09:00:02 3 Home // Результат 1970-01-01 09:00:03 3 Gift 1970-01-01 09:00:04 3 Basket ``` ``` sql 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 // Исходная точка 1970-01-01 09:00:03 1 Exit // Несовпадение с 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 // Исходная точка 1970-01-01 09:00:04 2 Basket // Несовпадение с Home 1970-01-01 09:00:01 3 Gift // Исходная точка 1970-01-01 09:00:02 3 Home // Совпадение с Home 1970-01-01 09:00:03 3 Gift // Результат 1970-01-01 09:00:04 3 Basket ``` **Поведение для `backward` и `last_match`** ``` sql 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 // Результат 1970-01-01 09:00:02 1 Gift // Исходная точка 1970-01-01 09:00:03 1 Exit 1970-01-01 09:00:01 2 Home 1970-01-01 09:00:02 2 Home // Результат 1970-01-01 09:00:03 2 Gift // Исходная точка 1970-01-01 09:00:04 2 Basket 1970-01-01 09:00:01 3 Gift 1970-01-01 09:00:02 3 Home // Результат 1970-01-01 09:00:03 3 Gift // Исходная точка 1970-01-01 09:00:04 3 Basket ``` ``` sql 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 // Совпадение с Home, результат `Null` 1970-01-01 09:00:02 1 Gift // Исходная точка 1970-01-01 09:00:03 1 Exit 1970-01-01 09:00:01 2 Home // Результат 1970-01-01 09:00:02 2 Home // Совпадение с Home 1970-01-01 09:00:03 2 Gift // Исходная точка 1970-01-01 09:00:04 2 Basket 1970-01-01 09:00:01 3 Gift // Результат 1970-01-01 09:00:02 3 Home // Совпадение с Home 1970-01-01 09:00:03 3 Gift // Исходная точка 1970-01-01 09:00:04 3 Basket ``` **Поведение для `base_condition`** ``` sql 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'); ``` ``` sql 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 // Начало не может быть исходной точкой, поскольку столбец ref не соответствует '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 ``` ``` sql 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 // Конец не может быть исходной точкой, поскольку столбец ref не соответствует 'ref4'. ``` ``` sql 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 // Эта строка не может быть исходной точкой, поскольку столбец ref не соответствует 'ref3'. 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 ``` ``` sql 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 // Результат 1970-01-01 09:00:03 1 B ref2 // Исходная точка 1970-01-01 09:00:04 1 B ref1 // Эта строка не может быть исходной точкой, поскольку столбец ref не соответствует 'ref2'. ```