窗口函数不减少行数,因其在保留原始每行基础上附加计算结果,不折叠、不分组、不丢弃行;而group by是聚合操作,必须将多行归并为一行。

窗口函数不减少行数,是因为它不改变数据集结构
窗口函数(如 ROW_NUMBER()、AVG() OVER (...))本质是在保留原始每一行的基础上,为每行“附加”一个计算结果。它不折叠、不分组、不丢弃任何行——这和 GROUP BY 的语义完全不同。GROUP BY 是聚合操作,必须把多行归并成一行(除非用 GROUPING SETS 等扩展),而窗口函数只是“就地计算”,行粒度始终是原始表的行粒度。
GROUP BY 要求非分组列必须聚合,窗口函数没有这个限制
写 SELECT a, b, COUNT(*) FROM t GROUP BY a 时,b 列不能直接出现(会报错:column "b" must appear in the GROUP BY clause or be used in an aggregate function)。但窗口函数允许你同时输出 a、b 和 COUNT(*) OVER (PARTITION BY a) ——因为没触发分组语义,所有原始列都天然可选。
常见错误现象:
- 误以为
SELECT *, SUM(x) OVER (PARTITION BY y)会像GROUP BY y那样只返回 y 的去重值 - 在需要降维统计的场景(如“每个部门最高薪员工”)中,仅靠窗口函数不加
WHERE或QUALIFY就直接取结果,导致返回全部员工而非筛选后的一行
真正实现“分组+保留明细”的组合用法
想既按组统计、又保留原始行信息(比如给每行标上“所在组内排名”或“组均值”),窗口函数是唯一简洁解法;但若目标是“只取每组一条代表记录”,就必须叠加过滤逻辑:
- PostgreSQL / Snowflake / BigQuery 支持
QUALIFY ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) = 1 - MySQL 8.0+ 同样支持
QUALIFY;旧版本需嵌套子查询或用JOIN关联聚合结果 - 注意
ORDER BY在窗口定义中的必要性:没指定排序的ROW_NUMBER()行为未定义,不同执行可能返回不同“第一行”
性能与物化差异:窗口函数通常比多次 JOIN 更高效
如果用 GROUP BY 先算出组均值,再 JOIN 回原表,数据库要走两趟扫描 + 哈希连接;而 AVG(salary) OVER (PARTITION BY dept) 一般只需单次扫描 + 滑动窗口累加(尤其当 PARTITION BY 列有索引或已排序时)。但反过来说,如果窗口帧(ROWS BETWEEN ...)定义复杂,或分区键基数极高(如每行都不同),反而可能拖慢执行。
容易被忽略的一点:窗口函数的结果是**不可索引的虚拟列**,无法用于 WHERE 过滤(除非用 QUALIFY 或外层查询),而 GROUP BY 输出的是物理可过滤的行集。










