窗口函数是保留原始行数的唯一方式,group by会压缩行数且无法同时返回明细与聚合值;avg(salary) over(partition by dept)可直接为每行员工添加本部门平均薪资,而group by无法实现该需求。

需要保留原始行时,窗口函数是唯一选择
GROUP BY 会把多行压缩成一行,比如 100 条员工记录按部门分组后只剩 3 行;而窗口函数输出行数恒等于输入行数。如果你要查“每个员工姓名、部门、薪资,同时显示本部门平均薪资”,AVG(salary) OVER(PARTITION BY dept) 是唯一直接解法。GROUP BY dept 无法同时返回 name 和 AVG(salary),MySQL 8.0+ 会直接报 ERROR 1055,PostgreSQL 拒绝执行。
要取每组最新/最旧完整记录,别用 GROUP BY
GROUP BY 后的 ORDER BY 只影响最终结果排序,不控制分组内顺序,所以它根本不能可靠取“每个用户的最新订单”。常见错误是写 SELECT user_id, MAX(created_at), order_id FROM orders GROUP BY user_id——order_id 值不确定,可能来自任意一条同用户记录。
- 必须用
ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY created_at DESC, id DESC)打标 - 必须套子查询或 CTE,因为窗口函数不能出现在
WHERE中:WHERE rn = 1会报错 - 漏写
ORDER BY:MySQL 可能返回不稳定序号,PostgreSQL 直接报错
PARTITION BY 写错等于白写
漏掉 PARTITION BY,AVG(salary) OVER() 就变成全表平均,每行都填同一个数,业务上毫无意义。这不是“少分组”,而是“完全不分组”。
-
AVG(salary) OVER(PARTITION BY dept)→ 每个部门单独算均值 -
AVG(salary) OVER()→ 全表一个数,等价于常量列 -
PARTITION BY字段没索引,尤其高基数(如user_id)时,WindowAgg节点容易成性能瓶颈
多个聚合指标共存时,窗口函数更简洁且可能更高效
想在同一查询里输出“每个客户的订单数、总金额、最大单笔金额”,用 GROUP BY customer_id 需要三趟扫描或复杂子查询;而窗口函数可并列写:
SELECT name, customer_id, COUNT(*) OVER(PARTITION BY customer_id), SUM(amount) OVER(PARTITION BY customer_id), MAX(amount) OVER(PARTITION BY customer_id) FROM orders;
主流引擎(MySQL 8.0+/PostgreSQL)通常复用一次分区扫描,不是各自扫三遍。但注意:如果三个 PARTITION BY 不一致(比如分别按 customer_id、region、1),优化器大概率会触发多次扫描——这点比 GROUP BY 更隐蔽,也更容易被忽略。










