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

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

来源:互联网 2026-04-27 22:52:08

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 所“看到”的数据,已经是经过层层过滤和聚合之后的结果了——它不会,也不可能回溯到最原始的表数据。这一点在编写复杂分析查询时,必须时刻牢记。

侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述

相关攻略

更多

热游推荐

更多
湘ICP备14008430号-1 湘公网安备 43070302000280号
All Rights Reserved
本站为非盈利网站,不接受任何广告。本站所有软件,都由网友
上传,如有侵犯你的版权,请发邮件给xiayx666@163.com
抵制不良色情、反动、暴力游戏。注意自我保护,谨防受骗上当。
适度游戏益脑,沉迷游戏伤身。合理安排时间,享受健康生活。