某些聚合函数不仅可以接受作为实参的列 (用于压缩) ,还可以接受一组参数——即用于初始化的常量。其语法使用两对括号,而不是一对:第一对用于参数,第二对用于实参。
计算自适应直方图。结果不保证精确。
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(pattern)(timestamp, cond1, cond2, ...)
在同一秒发生的事件在序列中的先后顺序可能不确定,这会影响结果。
参数
-
timestamp — 视为包含时间数据的列。典型数据类型为 Date 和 DateTime。你也可以使用任何受支持的 UInt 数据类型。
-
cond1, cond2 — 描述事件链的条件。数据类型:UInt8。最多可传入 32 个条件参数。函数只会考虑这些条件所描述的事件。如果序列中包含未在任何条件中描述的数据,函数会跳过它们。
参数
返回值
类型: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(pattern)(timestamp, cond1, cond2, ...)
参数
-
timestamp — 视为包含时间数据的列。典型的数据类型有 Date 和 DateTime。你也可以使用任何受支持的 UInt 数据类型。
-
cond1, cond2 — 用于描述事件链的条件。数据类型:UInt8。最多可以传入 32 个条件参数。函数只会考虑这些条件中描述的事件。如果序列中包含未被任何条件描述的数据,函数会跳过这些数据。
参数
pattern — 模式字符串。请参见 模式语法。
返回值
类型: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(pattern)(timestamp, cond1, cond2, ...)
参数
-
timestamp — 视为包含时间数据的列。典型的数据类型包括 Date 和 DateTime。您也可以使用任意受支持的 UInt 数据类型。
-
cond1, cond2 — 用于描述事件链的条件。数据类型:UInt8。最多可传入 32 个条件参数。函数只会考虑这些条件中描述的事件。如果序列中包含未在任何条件中描述的数据,函数会跳过这些数据。
参数
pattern — 模式字符串。请参见 模式语法。
返回值
- 事件链中与条件参数 (?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] │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
另请参阅
在滑动时间窗口中搜索事件链,并计算该事件链内已发生事件的最大数量。
该函数按以下算法工作:
-
函数会搜索满足事件链中第一个条件的数据,并将事件计数器设为 1。此时滑动窗口开始计时。
-
如果事件链中的事件在窗口内按顺序发生,则计数器会递增;如果事件顺序被打断,则计数器不会递增。
-
如果数据中存在多个完成程度不同的事件链,函数只会输出最长事件链的长度。
语法
windowFunnel(window, [mode, [mode, ... ]])(timestamp, cond1, cond2, ..., condN)
Arguments
timestamp — 包含时间戳的列名。支持的数据类型:Date、DateTime 以及其他无符号整数类型 (注意,虽然 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
判断给定时间段是否足以让用户在网店中挑选一部手机并购买两次。
设置以下事件链:
- 用户登录其网店账户 (
eventID = 1003) 。
- 用户搜索手机 (
eventID = 1007, product = 'phone') 。
- 用户下单 (
eventID = 1009) 。
- 用户再次下单 (
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 │
└───────┴───┘
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;
该函数接受一组条件作为参数,参数数量为 1 到 32 个,类型均为 UInt8,用于指示某个事件是否满足某一条件。
任何条件都可以作为参数指定 (如 WHERE 中所示) 。
除第一个条件外,其余条件均与第一个条件成对应用:如果第一个和第二个条件都为真,则第二个结果为真;如果第一个和第三个条件都为真,则第三个结果为真;以此类推。
语法
retention(cond1, cond2, ..., cond32);
参数
cond — 返回 UInt8 结果 (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 之间某个特定时间段内访问网站的独立访客数量 (cond1 和 cond2 条件) 。
r3- 在 2020-01-01 和 2020-01-03 的某个特定时间段内访问网站的独立访客数量 (cond1 和 cond3 条件) 。
计算参数中不同值的数量,最多到指定上限 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 个不同用户使用过的搜索关键词列表。
此函数的行为与 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 表,向其中 insert 一些数据,然后使用 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)) │
└──────────────────────┴────────────────────────────────────┘
返回与某个事件链匹配的下一个事件的值。
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
返回值
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 // 基准点,与 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 的行为
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
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 // 基准点
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
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 // 结果
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' 不匹配。