文章摘要:Clickhouse中高阶函数的一写常见使用案例,包含滑动窗口计算、分组计算TopK值,时序数据求diff、漏斗函数、留存函数
1、创建表
CREATE TABLE test_windows_function
(
`dt` Date,
`vales` Int64
)
ENGINE = Memory
2、插入数据
insert into test_windows_function(dt,vales) values('2020-01-01',5),('2020-01-02',10),('2020-01-03',15),('2020-01-04',20),('2020-01-05',25),('2020-01-06',30),('2020-01-07',35),('2020-01-08',40),('2020-01-09',45),('2020-01-10',50)
3、查看数据
┌─────────dt─┬─vales─┐
│ 2020-01-01 │ 5 │
│ 2020-01-02 │ 10 │
│ 2020-01-03 │ 15 │
│ 2020-01-04 │ 20 │
│ 2020-01-05 │ 25 │
│ 2020-01-06 │ 30 │
│ 2020-01-07 │ 35 │
│ 2020-01-08 │ 40 │
│ 2020-01-09 │ 45 │
│ 2020-01-10 │ 50 │
└────────────┴───────┘
4、预期效果
求top3(N)的最大值、最小值、平均值、求和
┌─────────dt─┬─vales─┬─max─┬─min─┬─ave─┬─sum─┐
│ 2020-01-01 │ 5 │ 15 │ 5 │ 10 │ 30 │
│ 2020-01-02 │ 10 │ 20 │ 10 │ 15 │ 45 │
│ 2020-01-03 │ 15 │ 25 │ 15 │ 20 │ 60 │
│ 2020-01-04 │ 20 │ 30 │ 20 │ 25 │ 75 │
│ 2020-01-05 │ 25 │ 35 │ 25 │ 30 │ 90 │
│ 2020-01-06 │ 30 │ 40 │ 30 │ 35 │ 105 │
│ 2020-01-07 │ 35 │ 45 │ 35 │ 40 │ 120 │
│ 2020-01-08 │ 40 │ 50 │ 40 │ 45 │ 135 │
│ 2020-01-09 │ 45 │ 50 │ 45 │47.5 │ 95 │
│ 2020-01-10 │ 50 │ 50 │ 50 │ 50 │ 50 │
└────────────┴───────┴─────┴─────┴─────┴─────┘
SQL逻辑
WITH
(
SELECT groupArray(vales)
FROM
(
SELECT vales
FROM test_windows_function
ORDER BY dt ASC
)
) AS vales_arr
SELECT
vales_arr,
arrayMap(shift -> arrayReduce('avg', arraySlice(vales_arr, shift + 1, 3)), range(length(vales_arr))) AS avg,
arrayMap(shift -> arrayReduce('max', arraySlice(vales_arr, shift + 1, 3)), range(length(vales_arr))) AS max,
arrayMap(shift -> arrayReduce('min', arraySlice(vales_arr, shift + 1, 3)), range(length(vales_arr))) AS min,
arrayMap(shift -> arrayReduce('sum', arraySlice(vales_arr, shift + 1, 3)), range(length(vales_arr))) AS sum
Row 1:
──────
vales_arr: [5,10,15,20,25,30,35,40,45,50]
avg: [10,15,20,25,30,35,40,45,47.5,50]
max: [15,20,25,30,35,40,45,50,50,50]
min: [5,10,15,20,25,30,35,40,45,50]
sum: [30,45,60,75,90,105,120,135,95,50]
SELECT
groupArrayMovingSum(3)(vales) AS sum,
groupArrayMovingAvg(3)(vales) AS avg
FROM test_windows_function
┌─sum───────────────────────────────┬─avg───────────────────────────┐
│ [5,15,30,45,60,75,90,105,120,135] │ [1,5,10,15,20,25,30,35,40,45] │
└───────────────────────────────────┴───────────────────────────────┘
1、数据量大的时候效果性能必然是不好的~~
2、方案二是19版本出来的新函数,目前支持sum和avg哦~,并不支持max、min
1、创建表
CREATE TABLE test_topK_function ( c_a Int32,c_b Int32,c_c Int32) ENGINE = Memory
2、插入数据
insert into test_topK_function ( c_a,c_b,c_c) values (1,2,5),(1,2,4),(1,3,3),(1,3,2),(1,4,1),(2,3,9),(2,3,7),(2,3,5),(2,4,3),(2,5,6),(3,3,9),(3,3,7),(3,3,5),(3,4,3),(3,5,6);
3、查看展示
SELECT *
FROM test_topK_function
ORDER BY c_a ASC
┌─c_a─┬─c_b─┬─c_c─┐
│ 1 │ 2 │ 5 │
│ 1 │ 2 │ 4 │
│ 1 │ 3 │ 3 │
│ 1 │ 3 │ 2 │
│ 1 │ 4 │ 1 │
│ 2 │ 3 │ 9 │
│ 2 │ 3 │ 7 │
│ 2 │ 3 │ 5 │
│ 2 │ 4 │ 3 │
│ 2 │ 5 │ 6 │
│ 3 │ 3 │ 9 │
│ 3 │ 3 │ 7 │
│ 3 │ 3 │ 5 │
│ 3 │ 4 │ 3 │
│ 3 │ 5 │ 6 │
└─────┴─────┴─────┘
4、预期效果
根据c_a列进行分组,找c_c列的top2
┌─c_a─┬─topK(2)(c_c)─┐
│ 3 │ [9,7] │
│ 2 │ [9,7] │
│ 1 │ [5,4] │
└─────┴──────────────┘
直接使用提供的topK函数实现
SELECT
c_a,
topK(2)(c_c)
FROM test_topK_function
GROUP BY c_a
┌─c_a─┬─topK(2)(c_c)─┐
│ 3 │ [9,7] │
│ 2 │ [9,7] │
│ 1 │ [5,4] │
└─────┴──────────────┘
下面的这个SQL是上面的topK实现的逻辑
SELECT
c_a,
groupArray(2)(c_c)
FROM
(
SELECT *
FROM test_topK_function
ORDER BY
c_a ASC,
c_c DESC
)
GROUP BY c_a
┌─c_a─┬─groupArray(2)(c_c)─┐
│ 3 │ [9,7] │
│ 2 │ [9,7] │
│ 1 │ [5,4] │
└─────┴────────────────────┘
1、创建表
CREATE TABLE default.test_ts
(
`uid` UInt64,
`ts` Int64,
`load` Float64
)
ENGINE = Memory
# 注意这里的时间字段只能是Int64
# clickhouse版本必须>=19
2、插入数据
insert into test_ts(uid,ts,load) values (1,1000,101.1),(1,1001,201.1),(1,1002,301.1),(1,1005,601.1),(1,1006,701.1),(1,1008,801.1);
3、查看数据
SELECT *
FROM test_ts
┌─uid─┬───ts─┬──load─┐
│ 1 │ 1000 │ 101.1 │
│ 1 │ 1001 │ 201.1 │
│ 1 │ 1002 │ 301.1 │
│ 1 │ 1005 │ 601.1 │
│ 1 │ 1006 │ 701.1 │
│ 1 │ 1008 │ 801.1 │
└─────┴──────┴───────┘
4、预期效果
┌─uid─┬───ts─┬──load─┐──diff─┐
│ 1 │ 1000 │ 101.1 │ 100.0 │
│ 1 │ 1001 │ 201.1 │ 100.0 │
│ 1 │ 1002 │ 301.1 │ 100.0 │
│ 1 │ 1005 │ 601.1 │ 100.0 │
│ 1 │ 1006 │ 701.1 │ 100.0 │
│ 1 │ 1008 │ 801.1 │ 50.0 │
└─────┴──────┴───────┘ ──────┘
根据ts字段,求load字段的差值,注意1002-1005有时间跳变,会自动处理
SELECT timeSeriesGroupRateSum(uid, ts, load)
FROM test_ts
┌─timeSeriesGroupRateSum(uid, ts, load)───────────────────────────────────────────┐
│ [(1000,0),(1001,100),(1002,100.00000000000003),(1005,100),(1006,100),(1008,50)] │
└─────────────────────────────────────────────────────────────────────────────────┘
场景描述,登录 --> 查看详情 --> 收藏 --> 购买,查看有多少用户在给定的时间创建依次执行上述操作。
1、创建表
CREATE TABLE test_window_funnel( uid String, eventid String, eventTime UInt64) ENGINE = Memory
2、插入数据
insert into test_window_funnel(uid,eventid,eventTime) values ('A','login',20200101),('A','view',20200102),('A','buy',20200103),('B','login',20200101),('B','view',20200102),('C','login',20200101),('C','buy',20200102); ('A',)
3、查看数据
SELECT *
FROM test_window_funnel
┌─uid─┬─eventid─┬─eventTime─┐
│ A │ login │ 20200101 │
│ A │ view │ 20200102 │
│ A │ buy │ 20200103 │
│ B │ login │ 20200101 │
│ B │ view │ 20200102 │
│ C │ login │ 20200101 │
│ C │ buy │ 20200102 │
└─────┴─────────┴───────────┘
A用户:从login(登录) -> view(详情页) -> buy(购买) 花了3天
B用户:从login(登录) -> view(详情页) 花了2天
C用户:从login(登录) -> viewbuy(购买) 花了2天
4、预期效果
计算2天内完成 从login(登录) -> view(详情页) -> buy(购买) 的用户
┌─uid─┬─res─┐
│ B │ 2 │
│ C │ 1 │
│ A │ 3 │
└─────┴─────┘
A=3说明A用户3件事情都做了
SELECT
uid,
windowFunnel(2)(eventTime, eventid = 'login', eventid = 'view', eventid = 'buy') AS res
FROM test_window_funnel
GROUP BY uid
┌─uid─┬─res─┐
│ B │ 2 │
│ C │ 1 │
│ A │ 3 │
└─────┴─────┘
A login -> view -> buy 2天内3件事均满足,结果为3
B login -> view 2天内满足2件事,结果为2
C login -> buy 2天内按照顺序只满足1件事,结果为1
计算网站的留存率,比如某个新上的页面或者功能,用户会不会每天都来
1、创建表
复用上面的test_window_funnel表结构
2、插入数据
新增一些数据
insert into test_window_funnel(uid,eventid,eventTime) values ('A','view',20200101),('A','view',20200103),('A','view',20200104),('B','view',20200104),('B','view',20200101),('C','view',20200103),('C','view',20200102);
3、查看数据
SELECT *
FROM test_window_funnel
ORDER BY
uid ASC,
eventid ASC,
eventTime ASC
┌─uid─┬─eventid─┬─eventTime─┐
│ A │ buy │ 20200103 │
│ A │ login │ 20200101 │
│ A │ view │ 20200101 │
│ A │ view │ 20200102 │
│ A │ view │ 20200103 │
│ A │ view │ 20200104 │
│ B │ login │ 20200101 │
│ B │ view │ 20200101 │
│ B │ view │ 20200102 │
│ B │ view │ 20200104 │
│ C │ buy │ 20200102 │
│ C │ login │ 20200101 │
│ C │ view │ 20200102 │
│ C │ view │ 20200103 │
└─────┴─────────┴───────────┘
4、预期效果
# 20200101,20200102,20200103,20200104 用户是否访问view(详情页)
A [1,1,1,1] ~ 4天连续访问了
B [1,1,0,1] ~ 第3天没有访问
C [0,0,0,0] ~ 因为第一天没有访问,所有后面的全都是0
SELECT
uid,
retention(eventTime = 20200101, eventTime = 20200102, eventTime = 20200103, eventTime = 20200104) AS r
FROM test_window_funnel
WHERE eventid = 'view'
GROUP BY uid
┌─uid─┬─r─────────┐
│ B │ [1,1,0,1] │
│ C │ [0,0,0,0] │
│ A │ [1,1,1,1] │
└─────┴───────────┘