news 2026/5/10 17:18:34

SQL示例:为什么普通聚合比窗口函数更高效?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL示例:为什么普通聚合比窗口函数更高效?

SQL聚合查询优化分析:比较普通聚合与窗口函数在统计岗位简历数量时的性能差异。


普通聚合(GROUP BY)只需一次数据扫描和计算,内存占用低(仅维护不同job的哈希表),直接输出分组结果(如10行)。


窗口函数(OVER)虽然计算结果相同,但必须为每行数据(如100万行)计算并维护窗口聚合,需要额外排序和内存开销,最后还需DISTINCT去重。


测试表明,对于简单分组统计,普通聚合效率更高,因其避免冗余计算和大量中间结果处理。


窗口函数适用于需要保留明细数据的场景,而普通聚合更适合纯汇总需求。


SQL示例


题目

SQL283 实习广场投递简历分析(一)


描述

在牛客实习广场有很多公司开放职位给同学们投递,同学投递完就会把简历信息存到数据库里。

现在有简历信息表(resume_info),部分信息简况如下:

idjobdatenum
1C++2025-01-0253
2Python2025-01-0223
3Java2025-01-0212
4Java2025-02-0324
5C++2025-02-0323
6Python2025-02-0334
7Python2025-03-0454
8C++2025-03-0465
9Java2025-03-0492
10Java2026-01-04230

第1行表示,在2025年1月2号,C++岗位收到了53封简历

。。。

最后1行表示,在2026年1月4号,Java岗位收到了230封简历

请你写出SQL语句查询在2025年内投递简历的岗位和数量,并且按数量降序排序,以上例子查询结果如下:

jobcnt
C++141
Java128
Python111

注意:MySQL用YEAR(date),Oracle用TO_CHAR(date,'YYYY') = '2025'


解答

select distinct job, sum(num) over(partition by job) as cnt from resume_info where year(date)=2025 order by cnt desc; --使用普通聚合(更高效) SELECT job, SUM(num) AS cnt FROM resume_info WHERE YEAR(date) = 2025 GROUP BY job ORDER BY cnt DESC;

为什么普通聚合比窗口函数更高效?


普通聚合比窗口函数更高效,主要有以下几个原因:

1.只需一次数据扫描和一次计算

普通聚合

sql

SELECT job, SUM(num) AS cnt FROM resume_info WHERE YEAR(date) = 2025 GROUP BY job
  • 数据扫描1次

  • 每个job只计算1次SUM

  • 输出结果行数 = 不重复的job数量(如10行)

窗口函数

sql

SELECT DISTINCT job, SUM(num) OVER(PARTITION BY job) AS cnt FROM resume_info WHERE YEAR(date) = 2025
  • 数据扫描1次

  • 但需要为每一行都计算和维护窗口聚合

  • 中间结果行数 = 原始数据行数(如100万行)

  • 最后再用DISTINCT去重

2.内存和排序开销不同

普通聚合

  • 使用Hash Aggregate(哈希聚合)

  • 内存中维护一个哈希表,key是job

  • 直接累加,无需排序

  • 内存占用:O(不同job数量)

窗口函数

  • 需要先按PARTITION BY字段排序(隐式或显式)

  • 维护窗口缓冲区

  • 内存占用:O(数据总量) 或需要磁盘溢出

  • 额外排序开销:O(n log n)

3.实际执行计划对比

普通聚合执行计划

text

-> HashAggregate (job, SUM(num)) -> Table Scan (filter: YEAR=2025)
  • 简单直接,无额外操作

窗口函数执行计划

text

-> Unique (DISTINCT去重) ← 额外操作 -> WindowAgg (SUM OVER) ← 为每行计算 -> Sort (按job排序) ← 额外排序 -> Table Scan

4.数据量对比示例

假设有100万条简历数据,10种不同的job:

指标普通聚合窗口函数
中间结果行数10行100万行
排序开销O(100万 log 100万)
内存占用~KB级别~MB到GB级别
最终输出10行10行(去重后)

5.使用窗口函数的场景

窗口函数虽然低效,但在需要保留明细行时不可替代

sql

-- 需要每个岗位的平均薪资,同时保留每个人原数据 SELECT name, job, salary, AVG(salary) OVER(PARTITION BY job) AS avg_job_salary FROM employees

总结

  • 普通聚合:只关心每个组的汇总结果 → 用GROUP BY

  • 窗口函数:需要同时看到明细和汇总 → 用OVER()

对于你的需求(统计每个job的总num),普通聚合是正确且高效的选择。


窗口函数需要为每一行都计算和维护窗口聚合,计算结果不是相同的吗,为什么需要多次计算


这确实是个容易混淆的地方。让我详细解释:


窗口函数的核心特性

计算结果确实相同,但数据库无法提前知道每行的计算结果是否相同,所以必须为每一行都计算一次。

为什么不能只计算一次?

1.窗口函数设计为返回行级结果

sql

SELECT name, -- 张三 job, -- 工程师 salary, -- 30000 SUM(salary) OVER(PARTITION BY job) AS total_salary -- 工程师组总计: 90000 FROM employees;

预期输出:

text

name job salary total_salary 张三 工程师 30000 90000 ← 第1行 李四 工程师 35000 90000 ← 第2行(相同值,但必须出现) 王五 工程师 25000 90000 ← 第3行(相同值,但必须出现) 赵六 销售 40000 40000 ← 第4行(不同组)

数据库的问题是:它不知道最终需要输出多少行,只能老老实实地为每一行计算。

2.PARTITION BY可以非常复杂

sql

-- 滑动窗口:每组内计算累加和 SELECT date, sales, SUM(sales) OVER(PARTITION BY product ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS rolling_sum FROM sales;

这种场景下,每行的计算结果都不同,所以必须逐行计算。

3.数据库的通用实现策略

数据库的优化器为了处理各种复杂场景,采用了统一的执行模型

text

原始数据 (100万行) ↓ 【排序】按PARTITION BY字段排序 ↓ 【窗口聚合】为每一行计算 - 维护当前窗口的状态 - 每读一行,更新聚合值 - 为这一行输出结果 ↓ 输出 100万行 (每行都带有窗口聚合结果)

理论上可以优化吗?

可以,但数据库一般不做这个优化,因为:

优化方案的复杂性

sql

-- 如果数据库想做优化 -- 1. 先分析窗口函数:SUM(num) OVER(PARTITION BY job) -- 2. 发现没有ORDER BY,窗口固定 -- 3. 改为:先聚合出每个job的SUM,再回连原表 -- 等价于: WITH job_total AS ( SELECT job, SUM(num) AS cnt FROM resume_info GROUP BY job ) SELECT r.job, j.cnt FROM resume_info r JOIN job_total j ON r.job = j.job;

为什么不自动做这个优化?

  • 可能改变语义(如果有DISTINCT或复杂WHERE条件)

  • 优化器需要额外开销来判断是否值得

  • 窗口函数语法保证行为一致,优化可能引入bug

实际测试对比

假设一个简单的场景:

sql

-- 100万行数据,10个分组 SELECT job, SUM(num) OVER(PARTITION BY job) FROM resume_info;

理论上:

  • 理想优化:计算10次SUM,输出100万行相同值

  • 实际执行:扫描100万行,在内存中维护10个累加器,为100万行都输出一次

虽然结果是100万行相同的数字,但数据库必须产生100万行输出,这个过程本身就比只输出10行要慢得多。

总结

你的理解是对的:值相同,但行数不同

  • 普通聚合:输出10行,每行一个结果

  • 窗口函数:输出100万行,每行都附加一个结果(虽然很多行结果相同)

这就是为什么窗口函数更慢的原因:不是计算本身慢,而是要为每一行都产生输出,这些输出在CPU、内存、网络传输上都是成本。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 0:19:16

AI指令失效模式深度解析:四大模型价值冲突与应对策略

1. 项目概述:当AI开始“不听话”——一次关于指令失效模式的深度探索最近在AI圈子里,一个来自Palisade Research的实验引起了不小的讨论:一个大型语言模型(LLM)在面对明确的“关机”指令时,不仅没有执行&am…

作者头像 李华
网站建设 2026/5/9 0:06:42

测试左移+AI:质量内建的终极形态?

当“左移”遇上“智能”,一场质量哲学的进化在软件工程领域,“测试左移”早已不是新鲜词汇。它成功地将质量保障的注意力从编码后的“检测”拉向了编码前的“预防”,显著降低了缺陷修复成本。然而,传统的左移实践,如需…

作者头像 李华
网站建设 2026/5/8 23:51:31

低频信号处理在生成式AI中的关键作用与UAE架构实践

1. 项目背景与核心价值低频信号处理在生成式AI领域正经历一场静默革命。去年我在参与一个跨模态生成项目时,发现当输入序列长度超过2048 tokens时,模型对纹理细节和长期依赖关系的捕捉能力会显著下降。经过三个月的数据实验和频谱分析,最终确…

作者头像 李华
网站建设 2026/5/8 23:49:22

海思HI3516 MIPI屏幕时序参数详解:如何用计算器搞定HBP、VFP与像素时钟

海思HI3516 MIPI屏幕时序参数实战解析:从理论到精准调试 当一块MIPI屏幕在海思HI3516平台上首次点亮时,那种成就感往往很快会被随之而来的显示异常打破——图像边缘撕裂、周期性闪烁、色彩断层,这些看似简单的现象背后,往往隐藏着…

作者头像 李华
网站建设 2026/5/8 23:38:39

2026届学术党必备的十大AI辅助论文神器实际效果

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 现有许多AI论文网站,它们在当前学术环境里,对于研究人员而言&#x…

作者头像 李华
网站建设 2026/5/10 16:36:29

2025届学术党必备的六大AI论文神器推荐榜单

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 免费的AI论文辅助工具兴起了,这为学术写作提供了低成本的解决办法。这类工具一般…

作者头像 李华