案例一:滑動窗口計算
需求描述
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語句按照dt字段排序,得到原始數據 [5,10,15,20,25,30,35,40,45,50]
- arraySlice(vales_arr, shift + 1, 3),每次都去前三個數。eg:第一次 [5,10,15]
- arrayReduce('avg', arraySlice(vales_arr, shift + 1, 3),計算平均數
- arrayMap函數,類似做個for循環
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] │
└───────────────────────────────────┴───────────────────────────────┘
TIPS
1、數據量大的時候效果性能必然是不好的~~
2、方案二是19版本出來的新函數,目前支持sum和avg哦~,並不支持max、min
案例二:分組計算TopK
需求描述
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] │
└─────┴────────────────────┘
TIPS
- 直接使用topK吧,原生提供。
案例三:時序數據求差值timeSeriesGroupRateSum
需求描述
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)] │
└─────────────────────────────────────────────────────────────────────────────────┘
TIPS
- clicktimeSeriesGroupRateSum很強大,求diff的場景非常常見,比如監控取得累加值,需要求差
- clicktimeSeriesGroupRateSum不僅僅是針對單uid,還可以處理多uid,也就是多個維度的數據對其
案例四:漏斗函數windowFunnel
需求描述
場景描述,登錄 --> 查看詳情 --> 收藏 --> 購買,查看有多少用戶在給定的時間創建依次執行上述操作。
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
TIPS
- 主要是計算轉化和留存率的,使用上也比較方便
案例五:留存函數retention
需求描述
計算網站的留存率,比如某個新上的頁面或者功能,用戶會不會每天都來
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] │
└─────┴───────────┘
TIPS
- retention函數使用場景比較多,不僅僅是用來計算留存率