ClickHouse 窗口函数详解:告别 GROUP BY 的局限性,实现灵活数据分析 - 若

news/2025/9/30 15:58:02/文章来源:https://www.cnblogs.com/zhanchenjin/p/19121321

什么是窗口函数?

窗口函数是 SQL 中一种强大的分析功能,它允许在对每一行进行计算时,能够访问到与当前行相关的多行数据。与 GROUP BY 不同,窗口函数不会将多行合并为一行,而是保留所有原始行,同时添加计算列。

直观理解

想象一个 Excel 表格:

 
 
姓名 部门 工资
张三 技术部 8000
李四 技术部 9000
王五 技术部 7500
赵六 销售部 7000

普通 GROUP BY:

sql
SELECT 部门, AVG(工资) as 平均工资 FROM 员工表 GROUP BY 部门;

结果:2行数据(部门聚合结果)

窗口函数:

sql
SELECT 姓名, 部门, 工资, AVG(工资) OVER (PARTITION BY 部门) as 部门平均工资 FROM 员工表;

结果:4行数据(保留所有原始行,新增计算列)

窗口函数的核心概念

1. 窗口定义

sql
函数名() OVER (PARTITION BY 分组字段ORDER BY 排序字段[窗口帧]
)
  • PARTITION BY:将数据分成多个窗口(类似 GROUP BY)

  • ORDER BY:在窗口内排序

  • 窗口帧:定义计算范围(如前后几行)

2. 常用窗口函数分类

排名函数

sql
-- 为每行分配唯一序号
ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC) as rn-- 排名,相同值会有并列
RANK() OVER (PARTITION BY department ORDER BY salary DESC) as rank-- 密集排名,无间隔
DENSE_RANK() OVER (PARTITION BY department ORDER BY salary DESC) as dense_rank

聚合函数

sql
-- 窗口内求和
SUM(salary) OVER (PARTITION BY department) as dept_total-- 窗口内平均值
AVG(salary) OVER (PARTITION BY department) as dept_avg-- 窗口内最大值
MAX(salary) OVER (PARTITION BY department) as dept_max

分布函数

sql
-- 百分比排名
PERCENT_RANK() OVER (ORDER BY salary) as pct_rank-- 累计分布
CUME_DIST() OVER (ORDER BY salary) as cume_dist

实战案例:解决数据去重问题

问题场景

在区块链任务表中,每个 (start_block, end_block) 组合可能有多个版本,我们需要获取每个组合的最新版本(created_at 最大的记录)。

传统方案的局限性

sql
-- GROUP BY 无法获取完整记录
SELECT start_block, end_block, MAX(created_at)
FROM block_tasks 
GROUP BY start_block, end_block;

问题:只能返回分组字段和聚合值,无法获取其他字段的完整信息。

窗口函数解决方案

sql
SELECT * FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY start_block, end_block ORDER BY created_at DESC) as rnFROM block_tasks WHERE status = 'init'
) WHERE rn = 1;

执行过程分解

原始数据:

 
 
id start_block end_block status created_at
1 1000 2000 init 2024-01-01 10:00:00
2 1000 2000 init 2024-01-02 15:00:00
3 1001 2001 init 2024-01-01 09:00:00
4 1001 2001 init 2024-01-03 14:00:00

窗口函数计算后:

 
 
id start_block end_block created_at rn
1 1000 2000 2024-01-01 10:00:00 2
2 1000 2000 2024-01-02 15:00:00 1
3 1001 2001 2024-01-01 09:00:00 3
4 1001 2001 2024-01-03 14:00:00 1
5 1001 2001 2024-01-02 11:00:00 2

最终结果(rn = 1):

 
 
id start_block end_block created_at
2 1000 2000 2024-01-02 15:00:00
4 1001 2001 2024-01-03 14:00:00

更多实用场景

场景1:计算移动平均

sql
-- 计算每行及前2行的平均价格
SELECT date, price,AVG(price) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) as moving_avg
FROM stock_prices;

场景2:计算累计和

sql
-- 计算每月销售额的累计和
SELECT month, sales,SUM(sales) OVER (ORDER BY month ROWS UNBOUNDED PRECEDING) as cumulative_sales
FROM monthly_sales;

场景3:前后行比较

sql
-- 比较当前行与前一行的差异
SELECT date, revenue,LAG(revenue) OVER (ORDER BY date) as prev_revenue,revenue - LAG(revenue) OVER (ORDER BY date) as growth
FROM daily_revenue;

ClickHouse 中的窗口函数

基本语法

sql
function_name([expression]) OVER ([PARTITION BY expression1, expression2, ...][ORDER BY expression1 [ASC|DESC], expression2 [ASC|DESC], ...][frame_specification]
)

性能优化建议

  1. 利用索引:确保 PARTITION BY 和 ORDER BY 的字段有合适索引

  2. 避免全表排序:使用 LIMIT 限制结果集大小

  3. 合理分区:数据分区可以减少单个窗口的数据量

ClickHouse 特定函数

sql
-- 获取每个分组的第一个值
first_value(column) OVER (PARTITION BY group ORDER BY time)-- 获取每个分组的最后一个值  
last_value(column) OVER (PARTITION BY group ORDER BY time)-- 计算分位数
quantile(0.5)(column) OVER (PARTITION BY group)

窗口函数 vs GROUP BY

 
 
特性 GROUP BY 窗口函数
输出行数 分组数量 原始行数
字段访问 只能访问分组字段和聚合值 可以访问所有原始字段
多个聚合 需要多个查询或复杂JOIN 单次查询可计算多个窗口
排序控制 无法控制选择哪条记录 明确指定排序和选择逻辑
性能 通常更快,但信息有限 稍慢,但功能更强大

实际代码示例

Go + GORM 实现

go
func GetLatestTasks(db *gorm.DB) ([]*BlockTask, error) {var tasks []*BlockTasksql := `SELECT * FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY start_block, end_block ORDER BY created_at DESC) as rnFROM block_tasks WHERE status = 'init') WHERE rn = 1ORDER BY start_block DESCLIMIT 100`err := db.Raw(sql).Scan(&tasks).Errorreturn tasks, err
}

总结

窗口函数是现代 SQL 中不可或缺的强大工具,它解决了 GROUP BY 的诸多限制:

  • ✅ 保留原始数据:不丢失任何行信息

  • ✅ 灵活分析:支持排名、聚合、分布等多种计算

  • ✅ 性能优秀:相比多次查询或复杂 JOIN,通常更高效

  • ✅ 代码简洁:用声明式语法替代复杂的过程逻辑

在数据去重、移动平均、排名计算、趋势分析等场景中,窗口函数都能提供优雅而高效的解决方案。掌握窗口函数,将极大提升你的数据分析能力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/923032.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单WEB网站

第一步:创建项目结构 在PyCharm中创建以下目录结构 my_web_app/ ├── backend/ # Flask后端代码 ├── frontend/ # Vue前端代码 ├── venv/ # Python虚拟环境(可选) └──requirements.…

dhru商城网站建设wordpress注册logo

目录 一、理论 1.K8S安全机制 2.Authentication认证 3.Authorization授权 4.Admission Control准入控制 5.User访问案例 6.ServiceAccount访问案例 二、实验 1.Admission Control准入控制 2.User访问案例 3.ServiceAccount访问案例 三、问题 1.生成资源报错 2.镜…

2025 年窗帘杆源头厂家最新推荐榜单:包含支架 / 环 / 全自动 / 可伸缩等多类产品及配件,帮助选到品质与交期双优的优质厂家

当前家居窗饰市场中,窗帘杆产品品质参差不齐、定制能力不足、供货效率低下等问题频发,让消费者和采购商在选择时倍感困扰。众多小型厂家缺乏专业研发团队,产品款式单一,无法满足家庭、写字楼、酒店等不同场景的个性…

大庆建设网站首页优化大师官方下载

1.链表的概念 对于顺序存储的结构最大的缺点就是插入和排序的时候需要移动大量的元素&#xff0c;所以链表的出生由此而来 先上代码&#xff1a; // 链表 public class LinkedList<T extends Comparable> {// 结点类class Node {T ele; // 当前结点上的元素内容Node ne…

2025 年电动窗帘厂家推荐榜单:聚焦国内优质企业定制实力与口碑,为采购者提供最新选择参考电动窗帘系统/电机/轨道/配件/智能电动窗帘厂家推荐

当下智能家居行业蓬勃发展,电动窗帘作为提升居住与办公舒适度的关键产品,市场需求持续攀升。然而,行业乱象却给采购者带来诸多困扰:部分品牌原材料选用不规范,劣质材料导致产品稳定性差、使用寿命短;定制服务能力…

网站推广的技巧wordpress 代码压缩

1.set和map存在的意义 &#xff08;1&#xff09;set和map的底层都是二叉搜索树&#xff0c;可以达到快速排序&#xff08;当我们按照迭代器的顺序来遍历set和map&#xff0c;其实是按照中序来遍历的&#xff0c;是排过序的&#xff09;、去重、搜索的目的。 &#xff08;2&a…

淘宝首页网站怎么做手机短视频制作软件app

目录 一&#xff0c;题目 二&#xff0c;思路 三&#xff0c;代码 一&#xff0c;题目 输入 共2行&#xff0c;第1行是被减数a&#xff0c;第2行是减数b(a > b)。每个大整数不超过200位&#xff0c;不会有多余的前导零。 输出 一行&#xff0c;即所求的差。 样例输入1…

网站建设公司业务望京做网站

目录频率域滤波基础频率域的其他特性频率域滤波基础知识频率域滤波步骤小结空间域和频率域滤波之间的对应关系频率域滤波基础 频率域的其他特性 频率域中的滤波过程如下&#xff1a; 首先修改傅里叶变换以在到特定目的然后计算IDFT&#xff0c;返回到空间域 # 频率域中的其…

专业微网站开发wordpress 用户登陆后跳转到首页

register_shutdown_function注册一个会在php中止时执行的函数&#xff0c;注册一个 callback &#xff0c;它会在脚本执行完成或者 exit() 后被调用。error_get_last获取最后发生的错误&#xff0c;包含type(错误类型)&#xff0c;message(错误消息)&#xff0c;file(发生错误所…

java类加载内存分析

java类加载内存分析加载内存分析 1.加载到内存(方法区(即特殊的堆)存放原始类,加载完成后会产生一个类对应java.lang.class对象代表不同的类存放到堆中,最后在栈中开始main()链接 2.链接,连接结束后静态变量默认…

Vue3 使用注意事项

1.script-setup语法糖下// 获取 emit,注意需要直接在setup范围下,放在具体方法里,可能会因作用域导致获取不到defineEmitsconst emit = defineEmits([quanping-comp]);

ClickHouse ReplacingMergeTree 去重陷阱:为什么你的 FINAL 查询无效? - 若

问题背景 在使用 ClickHouse 的 ReplacingMergeTree 引擎时,很多开发者会遇到一个困惑:明明使用了 FINAL 关键字,查询结果却仍然包含重复数据。比如这样的情况:数据库表err := db.Table(model.BlockTaskTableName)…

js中?? 和 || 的区别详解

?? 和 || 的区别详解 在 JavaScript/TypeScript 中,??(空值合并运算符)和 ||(逻辑或运算符)都用于提供默认值,但它们在处理不同值时有关键区别。 核心区别运算符 名称 触发条件 处理假值的方式?? 空值合并…

微信机器人API接口| 个人开发者必备

微信机器人API接口| 个人开发者必备 微信二次开发个人号api个人微信机器人开发api接口,微信个人号开发API在线接待更高效 在线沟通更快速、更有趣 语音回复 通过电脑端语音回复客户,提高效率 文件传输 支持文字、图片…

直击现场! “ 直通乌镇 ”开源赛复赛收官,OpenCSG担任评委,十强藏着哪些产业机会?

2025年9月16日,备受瞩目的“直通乌镇”全球互联网开源模型应用赛复赛在杭州圆满落幕。浙江省经济和信息化厅相关领导及各界专家、参赛团队代表齐聚一堂,共同见证了这一激动人心的时刻。值得关注的是,今年大赛首次设…

Python 列表生成式、字典生成式与生成器表达式

1. 列表生成式 (List Comprehension) 语法:[expression for item in iterable if condition] 示例:1.基本示例 # 创建平方数列表 squares = [x**2 for x in range(5)] print(squares) # [0, 1, 4, 9, 16]# 创建偶数…

java 解析json字符串,获取特定的字段值,JsonObject

java 解析json字符串,获取特定的字段值,JsonObjectjava 解析json字符串,获取特定的字段值package com.example.core.mydemo.java3;import com.google.gson.Gson; import com.google.gson.JsonObject; import com.go…

python 批量提取txt数据中的值写入csv

我有一堆雨滴谱txt数据,第一行是时间就是2024-05-10 10:21:00这样的格式,第二行是值,第三行是空格,然后第四行又是2024-05-10 10:21:00,第五行是值,第六行是空格,这样循环往复。给我写一个批量提取这些值的pyth…

【读书笔记】架构整洁之道 P5-2 软件架构 - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

嘉兴 企业网站 哪家厦门网站建设模板

效率工具 推荐一个程序员的常用工具网站&#xff0c;效率加倍嘎嘎好用&#xff1a;程序员常用工具 云服务器 云服务器限时免费领&#xff1a;轻量服务器2核4G腾讯云&#xff1a;2核2G4M云服务器新老同享99元/年&#xff0c;续费同价阿里云&#xff1a;2核2G3M的ECS服务器只需99…