窗口函数不删行只加列,GROUP BY 删行压缩数据;窗口函数须写 ORDER BY 保证顺序,不能用于 WHERE/HAVING,PARTITION BY 不可漏写,执行顺序在 WHERE 和 GROUP BY 之后。

GROUP BY 会删行,窗口函数只加列
GROUP BY 的结果行数一定 ≤ 原表行数,因为它把相同分组的多行“折叠”成一行;窗口函数不删不改原表结构,输出行数 = 输入行数,只是每行多一列计算结果。
常见错误现象:column "name" must appear in the GROUP BY clause or be used in an aggregate function —— 这不是语法写错了,而是你试图在 GROUP BY 后引用未聚合的明细字段,数据库直接拒绝。
- 要“每个部门一行,显示部门平均薪资”,用
SELECT dept, AVG(salary) FROM t GROUP BY dept - 要“每位员工一行,同时显示所在部门平均薪资”,必须用
AVG(salary) OVER (PARTITION BY dept),name和salary都能直接出现在 SELECT 里
ORDER BY 在窗口定义里不是可选,是业务前提
没写 ORDER BY 的窗口函数,多数场景下结果不可靠。尤其涉及顺序依赖时,比如排名、累计、前后行比较。
常见错误现象:ROW_NUMBER() OVER (PARTITION BY user_id) 在 PostgreSQL 直接报错;MySQL 可能每次执行编号不同,因为数据库按物理存储顺序编号,而非业务逻辑顺序。
-
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC)才能保证“每个客户最新订单排第 1” -
SUM(sales) OVER (PARTITION BY region ORDER BY date)算的是累计和;去掉ORDER BY就变成常量总和,二者语义完全不同 -
LAG(amount) OVER (ORDER BY order_date)是正确环比;用id排序会导致日期跳变或缺失时错位
窗口函数不能出现在 WHERE 或 HAVING 中
因为 SQL 执行顺序中,窗口函数在 WHERE 和 HAVING 之后才计算。你想过滤窗口结果,必须套一层子查询或 CTE。
常见错误现象:WHERE ROW_NUMBER() OVER (...) > 1 会报语法错误,数据库根本不会让你这么写。
- 想取“每个客户最近 3 笔订单”,得写:
SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS rn FROM orders ) t WHERE rn
- 想查“销售额高于本部门平均值的员工”,不能直接
HAVING salary > AVG(salary) OVER (PARTITION BY dept),得先算出平均值列,再在外层过滤
PARTITION BY 漏写就等于全表广播
PARTITION BY 不是装饰,是定义“谁跟谁比”的边界。漏写就等价于 OVER (),所有行在一个窗口里算,结果变成全表级统计值,业务逻辑直接跑偏。
常见错误现象:本想算“每个班级平均分”,却写了 AVG(score) OVER (),结果所有人显示同一个全校平均分,报表看起来“很整齐”,但完全失真。
- 按地区算累计销量:
SUM(sales) OVER (PARTITION BY region ORDER BY date) - 按用户算滚动 7 天均值:
AVG(amount) OVER (PARTITION BY user_id ORDER BY order_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) - 全表广播值(慎用):
COUNT(*) OVER ()是总行数,MAX(date) OVER ()是全局最大日期——这些确实有用,但必须明确知道你在广播
实际写的时候,最易被忽略的是执行顺序带来的约束:窗口函数看到的是已经过 WHERE、GROUP BY、HAVING 处理后的中间结果集。这意味着你可以在 GROUP BY 的聚合结果上再开窗,但反过来不行——窗口函数永远不能替代 GROUP BY 的压缩能力,也不能绕过它的前置限制。











