本文正在参加「技术专题19期 漫谈数据库技术」活动
前言
在使用窗口函数之前,我们先来了解一下什么叫做窗口函数(Window function)? 窗口函数在OLAP中使用的十分平常(如 Clickhouse、Hive等数仓),因为它对数据进行实时和离线分析表现都十分优秀。比如:
- a) 在排名的场景中,我们可以通过窗口函数得到用户的一个积分排名.
- b) 在TopN场景中,可以获取排名靠前的问题。掘金在热榜中,其实就是用了这个原理,展示点击+阅读中最多的Top20以内的用户文章。
正文
在了解到什么是窗口函数之后,我们来看看Clickhouse的一个使用。
窗口函数详解
我们来看看Clickhouse的窗口函数有哪些?
| 窗口函数 | 是否支持 |
|---|---|
count(*) over (partition by id order by time desc) | 支持 |
(count(*) over ()) / 2) | 支持 |
select ... from table window w as (partition by id)) | 支持 |
ROWS frame | 支持 |
RANGE frame | 支持 |
sum(value) over (order by time) | aggregate functions 支持 |
rank(), dense_rank(), row_number() | 支持 |
它的一个语法格式可以看下面:
select
aggregate_function (column_name)
OVER ([[PARTITION BY grouping_column] [ORDER BY sorting_column]
[ROWS or RANGE expression_to_bound_rows_withing_the_group]] | [window_name])
FROM table_name
WINDOW window_name as ([[PARTITION BY grouping_column] [ORDER BY sorting_column])
需要注意的是:
PARTITION BY: 它是对数据集进行分组的字段Order by: 它的在聚合函数计算之后进行一个排序操作Window: 允许重复使用窗口函数,所以从语法可以看到窗口在表之后使用了。
案例一使用
- 数据准备-创建对应的数据表和库 这个数据包含的是一个分区键、订单id以及订单的值。
CREATE TABLE wf_frame
(
`part_key` UInt64,
`value` UInt64,
`order` UInt64
)
ENGINE = Memory;
INSERT INTO wf_frame FORMAT Values
(1,1,1), (1,2,2), (1,3,3), (1,4,4), (1,5,5);
2.BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING使用 我们执行下面的sql:
SELECT
part_key,
value,
order,
groupArray(value) OVER (PARTITION BY part_key ORDER BY order ASC
Rows BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS frame_values
FROM wf_frame
ORDER BY
part_key ASC,
value ASC;
3、现在我们执行一个没有bound和order的sql:
SELECT
part_key,
value,
order,
groupArray(value) OVER (PARTITION BY part_key) AS frame_values
FROM wf_frame
ORDER BY
part_key ASC,
value ASC;
案例二使用
背景:加入我们需要实现Hive的窗口函数row number的效果 1、创建库表
CREATE TABLE default.row_number
(
`user_id` String,
`user_phone` String,
`create_time` DateTime,
`update_time` DateTime
)
ENGINE = MergeTree
ORDER BY user_id
SETTINGS index_granularity = 8192;
2、我们使用groupArray和groupby、orderby来实现,这个相比于直接使用row number来说是繁琐了点,但是这种方式实现是可以满足业务需求的。
select user_id,
groupArray(1)(user_phone) AS user_phone,
groupArray(1)(create_time) AS create_time,
groupArray(1)(update_time) AS update_time
from (select user_id,
user_phone,
create_time,
update_time
from default.row_number
ORDER BY update_time desc
) a
group by user_id
order by user_id
总结
本节主要是对CLickhouse的窗口函数的使用,当然还有一些不足的地方,我们可以去看看其他的博客和官方。我这里只是纪录自己在工作中遇到的问题,然后自己去模拟生产数据进行实践测试。