跳至主要内容

参数化聚合函数

一些聚合函数不仅可以接受参数列(用于压缩),还可以接受一组参数 - 用于初始化的常量。语法是两对括号而不是一对。第一个用于参数,第二个用于参数。

histogram

计算自适应直方图。它不保证精确的结果。

histogram(number_of_bins)(values)

该函数使用流式并行决策树算法。当新数据进入函数时,直方图 bin 的边界会进行调整。在一般情况下,bin 的宽度并不相等。

参数

values表达式,生成输入值。

参数

number_of_bins — 直方图中 bin 数的上限。函数会自动计算 bin 的数量。它会尝试达到指定的 bin 数,但如果失败,则会使用更少的 bin。

返回值

  • 以下格式的 数组元组

      ```
    [(lower_1, upper_1, height_1), ... (lower_N, upper_N, height_N)]
    ```

    - `lower` — Lower bound of the bin.
    - `upper` — Upper bound of the bin.
    - `height` — Calculated height of the bin.

示例

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 │ ██▌ │
└────────┴───────┘

在这种情况下,您应该记住,您不知道直方图 bin 的边界。

sequenceMatch

检查序列是否包含与模式匹配的事件链。

语法

sequenceMatch(pattern)(timestamp, cond1, cond2, ...)
注意

在同一秒内发生的事件可能以未定义的顺序出现在序列中,从而影响结果。

参数

  • timestamp — 被认为包含时间数据的列。典型的数据类型是 DateDateTime。您还可以使用任何支持的 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 — 被认为包含时间数据的列。典型的数据类型是 DateDateTime。您还可以使用任何支持的 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 │
└─────────────────────────────────────────────────────────────────────────┘

另请参阅

windowFunnel

在滑动时间窗口中搜索事件链,并计算发生在链中的最大事件数。

该函数根据以下算法工作

  • 该函数搜索触发链中第一个条件的数据,并将事件计数器设置为 1。这是滑动窗口开始的时刻。

  • 如果链中的事件按顺序在窗口内发生,则计数器会递增。如果事件序列被打断,则计数器不会递增。

  • 如果数据在不同完成点具有多个事件链,则该函数将仅输出最长链的大小。

语法

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

参数

  • timestamp — 包含时间戳的列的名称。支持的数据类型:DateDateTime 和其他无符号整数类型(请注意,即使时间戳支持 UInt64 类型,其值也不能超过 Int64 最大值,即 2^63 - 1)。
  • cond — 事件链的条件或数据。 UInt8

参数

  • window — 滑动窗口的长度,它是第一个和最后一个条件之间的时间间隔。window 的单位取决于 timestamp 本身并且是不同的。使用表达式 cond1 的时间戳 <= cond2 的时间戳 <= ... <= condN 的时间戳 <= cond1 的时间戳 + window 确定。
  • mode — 这是一个可选参数。可以设置一个或多个模式。
    • 'strict_deduplication' — 如果事件序列的条件相同,则此类重复事件会中断进一步处理。注意:如果多个条件对同一事件成立,则可能无法按预期工作。
    • 'strict_order' — 不允许其他事件介入。例如,在 A->B->D->C 的情况下,它在 D 处停止查找 A->B->C,并且最大事件级别为 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 年 1 月至 2 月期间可以完成链的进度。

查询

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. 使用 retention 函数按唯一 ID uid 对用户进行分组。

查询

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 之间特定时间段内访问过网站的唯一访客数量(cond1cond2 条件)。
  • r3 - 在 2020-01-01 和 2020-01-03 期间特定时间段内访问过网站的唯一访客数量(cond1cond3 条件)。

uniqUpTo(N)(x)

计算参数在指定限制 N 之前不同值的个数。如果不同参数值的个数大于 N,则此函数返回 N + 1,否则计算精确值。

建议用于较小的 N 值,最多 10。N 的最大值为 100。

对于聚合函数的状态,此函数使用等于 1 + N 的内存量。*一个值的大小(以字节为单位)。处理字符串时,此函数存储一个 8 字节的非加密哈希值;对字符串的计算是近似的。

例如,如果有一个表记录了用户在网站上进行的每次搜索查询。表中的每一行表示一个搜索查询,包含用户 ID、搜索查询和查询时间戳等列。可以使用 uniqUpTo 生成一个报告,该报告仅显示产生至少 5 个唯一用户的关键字。

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

uniqUpTo(4)(UserID) 计算每个 SearchPhrase 的唯一 UserID 值的数量,但它只计算最多 4 个唯一值。如果某个 SearchPhrase 的唯一 UserID 值超过 4 个,则函数返回 5(4 + 1)。然后,HAVING 子句过滤掉唯一 UserID 值数量少于 5 的 SearchPhrase 值。这将为您提供一个搜索关键字列表,这些关键字至少被 5 个唯一用户使用过。

sumMapFiltered

此函数的行为与 sumMap 相同,只是它还接受一个作为参数的用于过滤的键数组。当处理大量键时,这尤其有用。

语法

sumMapFiltered(keys_to_keep)(keys, values)

参数

  • 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)

参数

  • keys_to_keep:用于过滤的键的 数组
  • keys:键的 数组
  • values:值的 数组

返回值

  • 返回两个数组的元组:按排序顺序排列的键,以及对应键的总和值。

示例

在此示例中,我们创建了一个表 sum_map,向其中插入了一些数据,然后使用 sumMapFilteredWithOverflowsumMapFiltered 以及 toTypeName 函数来比较结果。在创建的表中,requests 的类型为 UInt8sumMapFiltered 将求和值的类型提升为 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 — 包含时间戳的列的名称。支持的数据类型:DateDateTime 和其他无符号整数类型。
  • event_column — 包含要返回的下一个事件值的列的名称。支持的数据类型:StringNullable(String)
  • base_condition — 基点必须满足的条件。
  • event1event2、... — 描述事件链的条件。 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 │
└────┴───────────┘

forwardhead 的行为

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

backwardtail 的行为

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

forwardfirst_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

backwardlast_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'.