游乐游手机版
首页/数据库/文章详情

SQL中PARTITION BY和GROUP BY有什么区别_窗口函数原理解析

时间:2026-04-25 12:43
GROUP BY压缩行数生成聚合结果,PARTITION BY保留原始行数仅附加计算值;前者用于统计报表,后者用于明细分析并支持窗口函数排序、排名等操作。 GROUP BY 会压缩行数,PARTITION BY 不改变行数 这可以说是两者最核心、最根本的区别。简单来说,GROUP BY 之后,你得到

GROUP BY压缩行数生成聚合结果,PARTITION BY保留原始行数仅附加计算值;前者用于统计报表,后者用于明细分析并支持窗口函数排序、排名等操作。

SQL中PARTITION BY和GROUP BY有什么区别_窗口函数原理解析

GROUP BY 会压缩行数,PARTITION BY 不改变行数

这可以说是两者最核心、最根本的区别。简单来说,GROUP BY 之后,你得到的结果集行数,会小于或等于原始数据中分组键的去重数量——数据被“压缩”了。而 PARTITION BY 则完全不同,它必须配合窗口函数(也就是 OVER())使用,其作用是在原有每一行数据上“附加”一个计算值,原始数据一条都不会少。

这里有个新手常踩的坑:有人会写 SELECT name, salary, A VG(salary) FROM emp GROUP BY name,本意是想查看每个人的工资以及他所在部门的平均工资。但实际上,这要么会报错,要么会产生逻辑错误。原因在于,A VG(salary) 是一个聚合值,name 是分组键,但查询中没有说明如何处理那些既非分组键、又非聚合函数的字段(比如这里的 salary 字段)。如果换成窗口函数的写法:A VG(salary) OVER (PARTITION BY dept_id),问题就迎刃而解了。这个写法不仅语法正确,还能在结果中保留每个人的 namesalarydept_id 等全部明细信息。

  • 记住一个铁律:GROUP BY 之后,SELECT 列表里只能出现两种东西:分组字段本身,或者包裹了聚合函数(如 SUM、A VG、COUNT)的表达式。
  • PARTITION BY 则出现在 OVER() 子句内部,它并不参与 SQL 标准执行顺序中的“分组”阶段。它的计算发生在 SELECT 产出结果之后,是一种“叠加”式的计算。
  • 从性能角度看:在大表上使用 GROUP BY,通常会触发哈希分组或排序操作,内存和磁盘的开销比较明显。而 PARTITION BY 在窗口计算阶段进行分区扫描,如果分区粒度很粗(比如整个表作为一个分区),那性能可能退化成全表遍历。

PARTITION BY 必须和 OVER() 一起用,不能单独存在

这一点必须明确:PARTITION BY 不是一个可以独立存在的 SQL 语句,它仅仅是 OVER() 子句的一个组成部分。如果你试图脱离 OVER() 单独使用它,比如写成 SELECT * FROM t PARTITION BY col,数据库会直接报错,告诉你语法非法。

来看一个典型的使用场景:如果你想为每个部门内的员工,按照薪资从高到低进行编号,同时还要保留所有原始字段,该怎么做?

SELECT emp_id, name, dept_id, salary,
       ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS rn
FROM employees;

在这段代码里,PARTITION BY dept_id 定义了“在每个部门内部进行独立编号”这个范围,ORDER BY salary DESC 指定了编号的顺序规则,而 ROW_NUMBER() 则是执行编号操作的窗口函数本身。

  • 漏写 OVER() 是初学者最高频的语法错误之一,常见的报错信息是 “missing window specification” 或类似提示。
  • PARTITION BY 子句可以为空(即不写),此时整个结果集会被视为一个大的分区。例如,COUNT(*) OVER() 会计算整个查询结果的总行数。
  • 多个 PARTITION BY 字段用逗号分隔,其语义等同于多维分组。比如 PARTITION BY region, year 表示先按地区分区,再在每个地区内按年份分区。

GROUP BY 的执行时机早于 PARTITION BY

理解 SQL 的执行顺序至关重要。在标准的 SQL 执行流程中,GROUP BY 发生在 HA VING 子句之前,但在 SELECT 列表确定之后。而包含 PARTITION BY 的窗口函数,其计算时机要晚得多——它是在最终的 SELECT 列确定之后才进行的。这意味着,窗口函数“看到”的数据,是已经经过 WHERE 过滤、GROUP BY 聚合(如果用了的话)之后的数据。

由此引出一个关键结论:你无法在同一个查询中,既用 GROUP BY 把数据压缩成汇总行,又指望 PARTITION BY 能对原始的、未聚合的明细行进行分区计算。这两者是互斥的。

  • 如果想同时得到部门汇总数据和员工明细数据怎么办?通常需要两个查询通过 UNION ALL 组合,或者使用公共表表达式(CTE)分开处理。
  • 在已经使用了 GROUP BY 的查询里嵌套窗口函数时,PARTITION BY 所分区的对象是聚合后的行,而非原始明细行。例如,SELECT dept_id, COUNT(*) cnt, SUM(cnt) OVER (PARTITION BY dept_id) 这个查询中,每个部门的 cnt 已经是一个单独的聚合值,再对它按部门分区求和已经没有实际意义。
  • 数据库兼容性需要注意:Oracle 和 PostgreSQL 等数据库支持在 GROUP BY 之后使用窗口函数,但 MySQL 直到 8.0 版本才完全支持这种组合。在旧版 MySQL(如 5.7)中尝试这么做会直接报错。

别把 PARTITION BY 当成 GROUP BY 的替代品

有些人看到 PARTITION BY 也能“按某列分开计算”,就试图用它来绕过 GROUP BYSELECT 列表的严格限制。比如,用 SELECT name, salary, A VG(salary) OVER (PARTITION BY dept_id) 来代替 GROUP BY dept_id。语法上这确实能执行,但两者的语义天差地别:前者返回所有员工的记录,并在每个人旁边附加其所在部门的平均工资;后者只返回每个部门一行数据,包含部门ID和该部门的平均工资。

选择依据其实很简单:问问自己,最终想要的是「汇总报告」还是「带有标记的明细」?

  • 如果你的目标是生成统计报表、进行数据聚合后导出 → 毫不犹豫地选择 GROUP BY
  • 如果你的需求是做排名、计算累计求和、进行前后行比较,或者在保留所有明细的同时增加统计列 → 那么 PARTITION BY 配合窗口函数就是你的最佳工具。
  • 当需要混合使用时,务必先确认数据库版本是否支持,尤其要警惕 MySQL 5.7 及更早版本完全不支持窗口函数。

最后,还有一个真正容易被忽略的细节,它源于执行顺序带来的隐性依赖:当你的查询变得复杂,同时包含了 WHERE 过滤、GROUP BY 聚合和窗口函数时,PARTITION BY 所“看到”的数据,已经是经过层层过滤和聚合之后的结果了——它不会,也不可能回溯到最原始的表数据。这一点在编写复杂分析查询时,必须时刻牢记。

来源:https://www.php.cn/faq/2347918.html
上一篇SQL如何处理嵌套查询中的重复列名冲突_使用别名规范化 下一篇如何使用SQL存储过程封装函数_统一业务逻辑提升复用性
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
金仓数据库逻辑备份实战:全库导出与模式替换全流程
数据库 · 2026-07-03

金仓数据库逻辑备份实战:全库导出与模式替换全流程

在长期的运维实践中,我越来越体会到,备份就像一份保险——平时看似无用,但关键时刻却是唯一的救命稻草。逻辑备份看似简单,可真正执行恢复时,各种陷阱接连浮现:表名大小写不一致、Schema 未正确切换、Owner 属性未同步修改……任何一个环节处理不当,最终恢复出的数据库就会与预期相去甚远。 本文将深入

金仓数据库sys_rman物理备份全流程演练与误覆盖恢复
数据库 · 2026-07-03

金仓数据库sys_rman物理备份全流程演练与误覆盖恢复

干运维这行,逻辑备份和物理备份我都接触过,但说句实在话,真正能在生产环境里扛住事儿的,还得是物理备份。逻辑备份导出的是 SQL 语句,数据量一大,那速度慢得让人抓狂,而且最关键的是,它没法做时间点恢复。物理备份不一样,它直接拷贝数据文件,再配上 WAL 归档日志,想恢复到过去哪一秒都行,这是它最硬核

Windows下将MySQL注册为系统自启服务教程
数据库 · 2026-07-03

Windows下将MySQL注册为系统自启服务教程

先说一个关键前提:务必以管理员身份运行终端,否则 mysqld --install 这条命令几乎不可能成功。问题不在于命令写错,而是 Windows 系统的用户账户控制(UAC)机制会在中途拦截——在普通 CMD 或 PowerShell 窗口执行这条命令,要么直接提示 Access is deni

Mac版Navicat中快速对比两个数据库的表结构异同
数据库 · 2026-07-03

Mac版Navicat中快速对比两个数据库的表结构异同

直接说结论:Mac 版 Navicat 和 Windows 版在表结构比对逻辑上完全一致。但默认配置下,它确实无法承受“全库一键比对上万张表”的压力。要想避免卡死、内存溢出、进度条永远停在 0%,你必须手动将表分批处理,或者利用前缀过滤来控制扫描范围。 为什么 Mac 上点击「结构同步」后界面会卡住

MySQL中UNION操作推荐用UNION ALL的原因
数据库 · 2026-07-03

MySQL中UNION操作推荐用UNION ALL的原因

MySQL中UNION与UNION ALL性能对比:别再被“保险”迷惑,差距远超预期 先给出核心结论:UNION ALL 的性能通常比 UNION 高出不止一个数量级。原因在于,UNION 在合并结果集后会自动触发去重操作,这往往伴随着隐式排序,进而产生临时表和文件排序。而 UNION ALL 则直