跳转到主要内容
某些聚合函数不仅可以接受作为实参的列 (用于压缩) ,还可以接受一组参数——即用于初始化的常量。其语法使用两对括号,而不是一对:第一对用于参数,第二对用于实参。

histogram

计算自适应直方图。结果不保证精确。
histogram(number_of_bins)(values)
该函数使用 A Streaming Parallel Decision Tree Algorithm。随着新数据进入函数,直方图各个 bin 的边界会动态调整。通常情况下,各 bin 的宽度并不相等。 参数 values — 生成输入值的 表达式 参数设置 number_of_bins — 直方图中 bin 数量的上限。函数会自动计算 bin 的数量。它会尽量达到指定的 bin 数量;如果无法达到,则会使用更少的 bin。 返回值
  • Tuple 组成的 Array,格式如下:
    [(lower_1, upper_1, height_1), ... (lower_N, upper_N, height_N)]
    
    • lower — bin 的下界。
    • upper — bin 的上界。
    • height — 计算得到的 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 │ ██▌   │
└────────┴───────┘
在这种情况下,需要注意的是,你并不知道直方图各个分箱的边界。

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 │
└──────┴────────┘
统计数字 1 之后出现数字 2 的次数,两者之间可以有任意数量的其他数字:
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 — 视为包含时间数据的列。典型的数据类型包括 DateDateTime。您也可以使用任意受支持的 UInt 数据类型。
  • cond1, cond2 — 用于描述事件链的条件。数据类型:UInt8。最多可传入 32 个条件参数。函数只会考虑这些条件中描述的事件。如果序列中包含未在任何条件中描述的数据,函数会跳过这些数据。
参数 返回值
  • 事件链中与条件参数 (?N) 匹配的时间戳数组。数组中的位置与 模式 中条件参数的位置对应。
类型:Array。 示例 假设 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)
Arguments
  • timestamp — 包含时间戳的列名。支持的数据类型:DateDateTime 以及其他无符号整数类型 (注意,虽然 timestamp 支持 UInt64 类型,但其值不能超过 Int64 的最大值,即 2^63 - 1) 。
  • cond — 描述事件链的条件或数据。UInt8
Parameters
  • 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 这种情况下,会在 D 处停止查找 A->B->C,最大事件级别为 2。
    • 'strict_increase' — 仅对时间戳严格递增的事件应用条件。
    • 'strict_once' — 在事件链中,每个事件只计数一次,即使它多次满足条件也是如此。
    • 'allow_reentry' — 忽略违反严格顺序的事件。例如,在 A->A->B->C 这种情况下,会通过忽略多余的 A 找到 A->B->C,最大事件级别为 3。
Returned value 滑动时间窗口内,事件链中连续触发条件的最大数量。 将分析查询结果中的所有事件链。 类型:Integer Example 判断给定时间段是否足以让用户在网店中挑选一部手机并购买两次。 设置以下事件链:
  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 月期间,在该事件链中最多能进行到哪一步。
Query
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;
Response
┌─level─┬─c─┐
│     4 │ 1 │
└───────┴───┘
allow_reentry 模式示例 本示例说明了 allow_reentry 模式如何与用户重入模式一起使用:
-- 样本数据:用户访问结账 -> 商品详情 -> 再次结账 -> 支付
-- 不使用 allow_reentry:停在第 2 级(商品详情页)
-- 使用 allow_reentry:到达第 4 级(完成支付)

SELECT
    level,
    count() AS users
FROM
(
    SELECT
        user_id,
        windowFunnel(3600, 'strict_order', 'allow_reentry')(
            timestamp,
            action = 'begin_checkout',      -- 第 1 步:开始结账
            action = 'view_product_detail', -- 第 2 步:查看商品详情
            action = 'begin_checkout',      -- 第 3 步:再次开始结账(重入)
            action = 'complete_payment'     -- 第 4 步:完成支付
        ) AS level
    FROM user_events
    WHERE event_date = today()
    GROUP BY user_id
)
GROUP BY level
ORDER BY level ASC;

retention

该函数接受一组条件作为参数,参数数量为 1 到 32 个,类型均为 UInt8,用于指示某个事件是否满足某一条件。 任何条件都可以作为参数指定 (如 WHERE 中所示) 。 除第一个条件外,其余条件均与第一个条件成对应用:如果第一个和第二个条件都为真,则第二个结果为真;如果第一个和第三个条件都为真,则第三个结果为真;以此类推。 语法
retention(cond1, cond2, ..., cond32);
参数
  • cond — 返回 UInt8 结果 (1 或 0) 的表达式。
返回值 由 1 或 0 组成的数组。
  • 1 — 事件满足条件。
  • 0 — 事件不满足条件。
类型:UInt8 示例 下面以一个计算 retention 函数来确定网站流量的示例进行说明。 1. 创建一个表作为示例。
Query
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);
输入表:
Query
SELECT * FROM retention_test
Response
┌───────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 对用户分组。
Query
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
Response
┌─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. 计算每天的网站访问总次数。
Query
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
)
Response
┌─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:用于过滤的 Array 键数组。
  • keys:键的 Array
  • values:值的 Array
返回值
  • 返回一个由两个数组组成的元组:按排序顺序排列的键,以及对应键汇总后的值。
示例
Query
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]);
Query
SELECT sumMapFiltered([1, 4, 8])(statusMap.status, statusMap.requests) FROM sum_map;
Response
   ┌─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_keepArray 类型的键数组,用于过滤。
  • keysArray 类型的键数组。
  • valuesArray 类型的值数组。
返回值
  • 返回一个包含两个数组的元组:按顺序排序的键,以及对应键求和后的值。
示例 在此示例中,我们创建一个 sum_map 表,向其中 insert 一些数据,然后使用 sumMapFilteredWithOverflowsumMapFilteredtoTypeName 函数来比较结果。在创建的表中,requests 的类型为 UInt8sumMapFiltered 会将求和值的类型提升为 UInt64 以避免溢出,而 sumMapFilteredWithOverflow 则保持为 UInt8,该类型不足以存储结果——也就是说,发生了溢出。
Query
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]);
Query
SELECT sumMapFilteredWithOverflow([1, 4, 8])(statusMap.status, statusMap.requests) as summap_overflow, toTypeName(summap_overflow) FROM sum_map;
Query
SELECT sumMapFiltered([1, 4, 8])(statusMap.status, statusMap.requests) as summap, toTypeName(summap) FROM sum_map;
Response
   ┌─sum──────────────────┬─toTypeName(sum)───────────────────┐
1. │ ([1,4,8],[10,20,10]) │ Tuple(Array(UInt8), Array(UInt8)) │
   └──────────────────────┴───────────────────────────────────┘
Response
   ┌─summap───────────────┬─toTypeName(summap)─────────────────┐
1. │ ([1,4,8],[10,20,10]) │ Tuple(Array(UInt8), Array(UInt64)) │
   └──────────────────────┴────────────────────────────────────┘

sequenceNextNode

返回与某个事件链匹配的下一个事件的值。 Experimental 函数,设置 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
Arguments
  • timestamp — 包含时间戳的列名。支持的数据类型:DateDateTime 以及其他无符号整数类型。
  • event_column — 包含要返回的下一个事件值的列名。支持的数据类型:StringNullable(String)
  • base_condition — 基准点必须满足的条件。
  • event1, event2, … — 描述事件链的条件。UInt8
返回值
  • event_column[next_index] — 如果模式匹配且下一个值存在。
  • NULL - 如果模式未匹配,或下一个值不存在。
类型:Nullable(String) 示例 当事件序列为 A->B->C->D->E,且你想知道 B->C 之后的事件 (即 D) 时,可以使用它。 用于查找 A->B 后续事件的查询语句:
Query
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;
Response
┌─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 // 基准点,与 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
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 // 基准点,未匹配到 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 // 基准点, Matched with Gift
1970-01-01 09:00:04    3   Basket // 基准点,匹配到 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 // 基准点
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  The result

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
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
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 // 结果
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
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 // 基准点
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 // 基准点
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 的行为
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 // head 不能作为基准点,因为 head 的 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
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 // tail 不能作为基准点,因为其 ref 列与 '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 // 此行不能作为基准点,因为 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
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' 不匹配。
最后修改于 2026年6月10日