窗口函数能保留原始行粒度并同时计算多级汇总,而group by会折叠行;sum/avg over配合partition by可实现个体、分类、全局三层聚合,order by+rows控制累计或滚动计算,count(distinct)不可直接用于窗口需拆解处理。

为什么 GROUP BY 不够用,必须上窗口函数?
当你需要在同一个查询里同时展示「明细行」和「跨维度汇总值」(比如每个销售员在各区域的销售额,还要附带该销售员总业绩、该区域总业绩、全公司平均业绩),GROUP BY 会强制折叠行,无法保留原始粒度。窗口函数不改变行数,只在每行“旁加”计算结果——这才是交叉报表的核心能力。
常见错误是试图用多层子查询或 JOIN 拼凑汇总字段,结果逻辑臃肿、性能差、NULL 处理混乱。窗口函数一条 SUM() OVER (PARTITION BY ...) 就能解决。
SUM() OVER 和 AVG() OVER 怎么配 PARTITION BY 实现多维切片?
关键不是堆函数,而是精准定义「按什么分组做局部汇总」。交叉报表通常要三个层级:个体(如销售员)、分类(如城市)、全局(全表)。对应写法如下:
SELECT salesperson, city, amount, SUM(amount) OVER (PARTITION BY salesperson) AS sp_total, -- 每个销售员的总和 SUM(amount) OVER (PARTITION BY city) AS city_total, -- 每个城市的总和 AVG(amount) OVER () AS overall_avg -- 全局平均(无 PARTITION BY 即全表) FROM sales;
注意:PARTITION BY 可以组合多个字段,比如 PARTITION BY salesperson, city 用于算「每人每城」的均值;但别漏掉括号——SUM(amount) OVER PARTITION BY city 会报错,必须写成 SUM(amount) OVER (PARTITION BY city)。
排序和范围控制:ORDER BY + ROWS BETWEEN 容易踩哪些坑?
如果报表要“累计求和”或“移动平均”,必须加 ORDER BY,否则结果不可预测(尤其在分布式数据库里)。但加了 ORDER BY 后,默认窗口变成“从第一行到当前行”,可能不是你想要的范围。
- 想算“截止当前月的累计销售额”:用
SUM(amount) OVER (PARTITION BY salesperson ORDER BY month ROWS UNBOUNDED PRECEDING) - 想算“近3个月滚动平均”:用
AVG(amount) OVER (PARTITION BY salesperson ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) - 错误示范:
ORDER BY month但没写ROWS子句 → 默认行为因数据库而异(PostgreSQL 是UNBOUNDED PRECEDING,MySQL 8.0+ 是UNBOUNDED PRECEDING,但旧版 MySQL 不支持窗口函数)
聚合函数嵌套窗口?别试了,COUNT(DISTINCT) OVER 不合法
SQL 标准不支持在窗口函数中使用 DISTINCT,所以 COUNT(DISTINCT user_id) OVER (PARTITION BY region) 会直接报错。替代方案只有两种:
— 如果数据量不大,先用子查询去重再聚合:SELECT region, COUNT(*) FROM (SELECT DISTINCT region, user_id FROM events) t GROUP BY region,然后 JOIN 回主表;
— 如果必须单条 SQL,用条件聚合模拟:COUNT(CASE WHEN rn = 1 THEN 1 END) OVER (PARTITION BY region),其中 rn 是按 user_id 分组的 ROW_NUMBER() OVER (PARTITION BY region, user_id ORDER BY id)。
真正难的不是语法,是意识到:窗口函数本质是“行级计算”,它没法突破聚合函数自身的语义限制。遇到 DISTINCT 类需求,得主动拆解步骤,而不是硬套窗口。











