【Clickhouse】Clickhouse 分析函数 window functions 窗口函数

138 篇文章 626 订阅 ¥19.90 ¥99.00


在这里插入图片描述

1.概述

转载:Clickhouse 分析函数 window functions 窗口函数

clickhouse 暂时么有提供标准SQL的分析函数,但是有计划添加此功能,预计2020年第四季度的早期实现。

在clickhouse 2020年第四季度之前可以使用clickhouse提供的函数来实现。

1.1.窗口函数:

Online Anallytical Processing,联机分析处理,可以对数据库数据进行实时分析处理

1.2.标准SQL语法

分析函数 overpartition by 列名 order by 列名 )

1.3.分析函数分类:

聚合类

avg(列名)sum(列名)count(列名)max(列名)min(列名)

排名类

  1. row_number() 按照值排序时产生一个自增编号,不会重复
    rank() 按照值排序时产生一个自增编号,值相等时会重复,会产生空位
  2. dense_rank() 按照值排序时产生一个自增编号,值相等时会重复,不会产生空位

注意:排名类分析函数不需要任何参数

其他类

  1. lag(列名,往前的行数,[行数为null时的默认值,不指定为null])
  2. lead(列名,往后的行数,[行数为null时的默认值,不指定为null])
  3. ntile(n) 用于将分组数据按照顺序切分成n片,返回当前切片值,如果切片不均匀,默认增加第一个切片的分布。

概述:
window function时 SQL:2003 新加入的标准。
常见的分析函数:

1.排名(rownumber,rank,dense_rank)
 
2.sum() over()
 
3.count() over()
 
4.lead()/lag() over()
 
5.ntile(n) over()
 

1.排名函数:

Clickhouse没有直接提供对应的开窗函数,需要使用clickhouse提供的函数来变相实现,主要涉及以下数组函数:

arrayEnumerate
arrayEnumerateDense
arrayEnumerateUniq
 

简单示例:

Clickhouse> with (select [100,200,300,200,400,500] ) as arr select arrayEnumerate(arr) as rownumber,arrayEnumerateDense(arr) as dense_rank,arrayEnumerateUniq(arr) as uniq_rank;
 
WITH 
    (
        SELECT [100, 200, 300, 200, 400, 500]
    ) AS arr
SELECT 
    arrayEnumerate(arr) AS rownumber, 
    arrayEnumerateDense(arr) AS dense_rank, 
    arrayEnumerateUniq(arr) AS uniq_rank
 
┌─rownumber─────┬─dense_rank────┬─uniq_rank─────┐
│ [1,2,3,4,5,6][1,2,3,2,4,5][1,1,1,2,1,1] │
└───────────────┴───────────────┴───────────────┘
 
1 rows in set. Elapsed: 0.004 sec.

arrayEnumerate 等同于row_number()
arrayEnumerateDense 等同于dense_rank()
arrayEnumerateUniq 只返回了元素第一次出现的次数

数据准备:

CREATE TABLE t_data
ENGINE = Memory AS
WITH 
    (
        SELECT ['A', 'A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', '90', '80', '90', '88', '90', '100', '77', '99', '90', '80']
    ) AS dict
SELECT 
    dict[(number % 10) + 1] AS id, 
    dict[number - 10] AS score
FROM system.numbers
LIMIT 10
 
SELECT *
FROM t_data
 
┌─id─┬─score─┐
│ A  │ 90    │
│ A  │ 80    │
│ A  │ 90    │
│ A  │ 88    │
│ B  │ 90    │
│ B  │ 100   │
│ B  │ 77    │
│ C  │ 99    │
│ C  │ 90    │
│ C  │ 80    │
└────┴───────┘
 
10 rows in set. Elapsed: 0.002 sec. 

– 先查询:

SELECT 
    id, 
    groupArray(score) AS arr, 
    arrayEnumerate(arr) AS rownumber, 
    arrayEnumerateDense(arr) AS dense_rank, 
    arrayEnumerateUniq(arr) AS uniq_rank
FROM 
(
    SELECT *
    FROM t_data
    ORDER BY score DESC
)
GROUP BY id
ORDER BY id ASC
 
┌─id─┬─arr───────────────────┬─rownumber─┬─dense_rank─┬─uniq_rank─┐
│ A  │ ['90','90','88','80'][1,2,3,4][1,1,2,3][1,2,1,1] │
│ B  │ ['90','77','100'][1,2,3][1,2,3][1,1,1]   │
│ C  │ ['99','90','80'][1,2,3][1,2,3][1,1,1]   │
└────┴───────────────────────┴───────────┴────────────┴───────────┘
 
3 rows in set. Elapsed: 0.004 sec. 

– 最终的查询:

SELECT 
    id, 
    score, 
    rownumber, 
    dense_rank, 
    uniq_rank
FROM 
(
    SELECT 
        id, 
        groupArray(score) AS arr, 
        arrayEnumerate(arr) AS rownumber, 
        arrayEnumerateDense(arr) AS dense_rank, 
        arrayEnumerateUniq(arr) AS uniq_rank
    FROM 
    (
        SELECT *
        FROM t_data
        ORDER BY score DESC
    )
    GROUP BY id
    ORDER BY id ASC
)
ARRAY JOIN 
    arr AS score, 
    rownumber, 
    dense_rank, 
    uniq_rank
ORDER BY 
    id ASC, 
    rownumber ASC, 
    dense_rank ASC
 
┌─id─┬─score─┬─rownumber─┬─dense_rank─┬─uniq_rank─┐
│ A  │ 90111 │
│ A  │ 90212 │
│ A  │ 88321 │
│ A  │ 80431 │
│ B  │ 90111 │
│ B  │ 77221 │
│ B  │ 100331 │
│ C  │ 99111 │
│ C  │ 90221 │
│ C  │ 80331 │
└────┴───────┴───────────┴────────────┴───────────┘
 
10 rows in set. Elapsed: 0.006 sec. 

可以看到rank()函数的还是没有实现。

2.Top N:

取每个用户的Top 2 的分数:

可以看到id的分数可能有重复,需要使用distinct去重。

Clickhouse> select id,score,d_rank from (select id,score,d_rank from(select id,groupArray(score) as arr,arrayEnumerateDense(arr) AS d_rank from(select distinct id,score from t_data order by score desc) group by id) array join arr as score,d_rank order by id asc,d_rank asc) where d_rank <3;
 
 
SELECT 
    id, 
    score, 
    d_rank
FROM 
(
    SELECT 
        id, 
        score, 
        d_rank
    FROM 
    (
        SELECT 
            id, 
            groupArray(score) AS arr, 
            arrayEnumerateDense(arr) AS d_rank
        FROM 
        (
            SELECT DISTINCT 
                id, 
                score
            FROM t_data
            ORDER BY score DESC
        )
        GROUP BY id
    )
    ARRAY JOIN 
        arr AS score, 
        d_rank
    ORDER BY 
        id ASC, 
        d_rank ASC
)
WHERE d_rank < 3
 
┌─id─┬─score─┬─d_rank─┐
│ A  │ 901 │
│ A  │ 882 │
│ B  │ 901 │
│ B  │ 772 │
│ C  │ 991 │
│ C  │ 902 │
└────┴───────┴────────┘
 
6 rows in set. Elapsed: 0.014 sec. 

3.同比环比计算:

同比:今年当月和去年的同一个月的数据,比如现在是2020年7月份,则需要和去年7月份的数据比较。英文名为Year-over-Year

环比:今年当月和今年的上一个月的数据比较,比如现在是2020年7月份则需要和2020年的6月份的数据比较,英文名为Month-over-Month

同比增长率= (本月数据-去年同月数据)/去年同月的数据

环比增长率=(本月数据 - 上月数据)/上月数据

在clickhouse中没有提供标准SQL种的lead 和lag函数。我们可以借助于clickhouse的提供的neighbor函数来实现。

neighbor(column, offset[, default_value])

The result of the function depends on the affected data blocks and the order of data in the block.
If you make a subquery with ORDER BY and call the function from outside the subquery, you can get the expected result.

Parameters

column — A column name or scalar expression.
offset — The number of rows forwards or backwards from the current row of column. Int64.
default_value — Optional. The value to be returned if offset goes beyond the scope of the block. Type of data blocks affected.
Returned values

Value for column in offset distance from current row if offset value is not outside block bounds.
Default value for column if offset value is outside block bounds. If default_value is given, then it will be used.
Type: type of data blocks affected or default value type.

参考:
https://clickhouse.tech/docs/en/sql-reference/functions/other-functions/
数据准备:

Clickhouse> with toDate('2019-01-01') as start_date select toStartOfMonth(start_date + (number*32)) month_start,(number+10)*100 amount from numbers(24);
 
WITH toDate('2019-01-01') AS start_date
SELECT 
    toStartOfMonth(start_date + (number * 32)) AS month_start, 
    (number + 10) * 100 AS amount
FROM numbers(24)
 
┌─month_start─┬─amount─┐
│  2019-01-011000 │
│  2019-02-011100 │
│  2019-03-011200 │
│  2019-04-011300 │
│  2019-05-011400 │
│  2019-06-011500 │
│  2019-07-011600 │
│  2019-08-011700 │
│  2019-09-011800 │
│  2019-10-011900 │
│  2019-11-012000 │
│  2019-12-012100 │
│  2020-01-012200 │
│  2020-02-012300 │
│  2020-03-012400 │
│  2020-04-012500 │
│  2020-05-012600 │
│  2020-06-012700 │
│  2020-07-012800 │
│  2020-08-012900 │
│  2020-10-013000 │
│  2020-11-013100 │
│  2020-12-013200 │
│  2021-01-013300 │
└─────────────┴────────┘
 
24 rows in set. Elapsed: 0.003 sec. 
 
 
WITH toDate('2019-01-01') AS start_date
SELECT 
    toStartOfMonth(start_date + (number * 32)) AS month_start, 
    (number + 10) * 100 AS amount, 
    neighbor(amount, -12) AS prev_year_amount, 
    neighbor(amount, -1) AS prev_month_amount, 
    if(prev_year_amount = 0, -999, round((amount - prev_year_amount) / prev_year_amount, 2)) AS year_over_year, 
    if(prev_month_amount = 0, -999, round((amount - prev_month_amount) / prev_month_amount, 2)) AS month_over_month
FROM numbers(24)
 
┌─month_start─┬─amount─┬─prev_year_amount─┬─prev_month_amount─┬─year_over_year─┬─month_over_month─┐
│  2019-01-01100000-999-999 │
│  2019-02-01110001000-9990.1 │
│  2019-03-01120001100-9990.09 │
│  2019-04-01130001200-9990.08 │
│  2019-05-01140001300-9990.08 │
│  2019-06-01150001400-9990.07 │
│  2019-07-01160001500-9990.07 │
│  2019-08-01170001600-9990.06 │
│  2019-09-01180001700-9990.06 │
│  2019-10-01190001800-9990.06 │
│  2019-11-01200001900-9990.05 │
│  2019-12-01210002000-9990.05 │
│  2020-01-012200100021001.20.05 │
│  2020-02-012300110022001.090.05 │
│  2020-03-0124001200230010.04 │
│  2020-04-012500130024000.920.04 │
│  2020-05-012600140025000.860.04 │
│  2020-06-012700150026000.80.04 │
│  2020-07-012800160027000.750.04 │
│  2020-08-012900170028000.710.04 │
│  2020-10-013000180029000.670.03 │
│  2020-11-013100190030000.630.03 │
│  2020-12-013200200031000.60.03 │
│  2021-01-013300210032000.570.03 │
└─────────────┴────────┴──────────────────┴───────────────────┴────────────────┴──────────────────┘
 
24 rows in set. Elapsed: 0.003 sec. 

结论:

3.案例

clickhouse数据模型之有序漏斗分析

参考:

参考:
https://en.wikipedia.org/wiki/SQL_window_function
https://en.wikipedia.org/wiki/SQL:2003

https://github.com/ClickHouse/ClickHouse/issues/9887
window view:
https://github.com/ClickHouse/ClickHouse/pull/8331

https://github.com/ClickHouse/ClickHouse/pull/5925
特殊函数:
sequenceMatch
windowfunnel

https://github.com/ClickHouse/ClickHouse/issues/5485
https://github.com/ClickHouse/ClickHouse/issues/5132
https://github.com/ClickHouse/ClickHouse/issues/1469

https://clickhouse.tech/docs/en/sql-reference/functions/other-functions/

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值