当前位置: 首页 > news >正文

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

什么是窗口函数?

窗口函数是 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.hskmm.com/?act=detail&tid=21550

相关文章:

  • 简单WEB网站
  • AtCoder AGC044 总结
  • UOJ#32【UR #2】跳蚤公路 题解
  • 2025 年窗帘杆源头厂家最新推荐榜单:包含支架 / 环 / 全自动 / 可伸缩等多类产品及配件,帮助选到品质与交期双优的优质厂家
  • 2025 年电动窗帘厂家推荐榜单:聚焦国内优质企业定制实力与口碑,为采购者提供最新选择参考电动窗帘系统/电机/轨道/配件/智能电动窗帘厂家推荐
  • Vue3 使用注意事项
  • ClickHouse ReplacingMergeTree 去重陷阱:为什么你的 FINAL 查询无效? - 若
  • js中?? 和 || 的区别详解
  • 微信机器人API接口| 个人开发者必备
  • 直击现场! “ 直通乌镇 ”开源赛复赛收官,OpenCSG担任评委,十强藏着哪些产业机会?
  • Python 列表生成式、字典生成式与生成器表达式
  • java 解析json字符串,获取特定的字段值,JsonObject
  • python 批量提取txt数据中的值写入csv
  • 回忆中学的函数
  • Java 一行一行的读取文本,小Demo 大学问
  • 家里wifi电信出口ip如何控制不变,解决访问云服务器上面的资源
  • 2025 年挤压造粒机源头厂家最新推荐榜单:前五企业技术实力、服务能力及口碑测评指南对辊挤压/化肥挤压/干粉挤压造粒机厂家推荐
  • MYSQL数据库取消表的约束
  • 2025 年京东 e 卡回收平台最新推荐排行榜:权威测评实时结算平台,助力用户安全高效转让京东 e 卡
  • 2025 年支付宝消费券回收平台最新推荐榜单:优质平台权威测评,助您高效安全处理闲置消费券支付宝消费券回收/闲置支付宝消费券回收/支付宝消费券快速回收平台推荐
  • ICP备案查询网站 域名备案查询
  • 2025 年注浆管厂家最新权威推荐排行榜:聚焦 R780/108 / 隧道 / 预埋 / 桩基等专用产品,精选 TOP5 优质企业
  • stable diffusion网络结构详解
  • 9.30
  • 网络与系统攻防技术实验一——逆向破解与Bof
  • 【python】解决grpcio.protoc生成的pb文件里面没有类和方法定义
  • 阙韩
  • “计算机配置\Windows 设置\安全设置\本地策略\审核策略” 配置后不生效
  • Spring Boot 事件发布与监听 观察者模式的实际应用 - 实践
  • P13969 [VKOSHP 2024] Exchange and Deletion