窗口函数必须写over(partition by ...)才能分组不丢行;空括号over()默认全表计算,结果错误;聚合函数加over后才成为窗口函数,输出行数与输入一致。

窗口函数怎么写才能不丢行
直接用 GROUP BY 会把明细行合并掉,窗口函数才是保留原始行数同时算聚合值的正解。关键不是“能不能用”,而是“怎么写才不出错”——尤其是别把 OVER() 写成空括号,那会默认按全表计算,结果常和预期不符。
-
OVER()必须带子句:至少指定PARTITION BY(分组)或ORDER BY(排序),两者可共存;空括号OVER()是语法合法但语义危险的写法 - 聚合类窗口函数(如
SUM()、COUNT()、AVG())必须配合OVER,否则报错或被当作普通聚合函数执行 - 如果只想要分组内累计值,用
ORDER BY配合ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW;若只要分组总数,就只写PARTITION BY,不加ORDER BY
count(*) over(partition by ...) 为什么返回 1?
常见错因是误把主表和关联表混在一起,导致 PARTITION BY 字段在 JOIN 后重复出现,实际每个分组只剩一行。比如用户表左连订单表,按 user_id 分组后,每个 user_id 对应多条订单,COUNT(*) OVER(PARTITION BY user_id) 才会返回该用户订单数;但如果 user_id 在结果里被去重或过滤,就只剩 1。
- 检查
SELECT中是否包含未参与PARTITION BY的唯一字段(如订单 ID),它会让每行天然独立,即使PARTITION BY user_id也无效 - 确认没有在
WHERE或HAVING中提前过滤掉同组多行数据 - 用
COUNT(1) OVER(PARTITION BY user_id)和COUNT(*) OVER(PARTITION BY user_id)行为一致,不用刻意换
sum() over() 和 group by + join 性能差多少?
窗口函数通常比先 GROUP BY 再 JOIN 回原表快,尤其当分组维度高、明细行多时。因为前者单次扫描完成计算,后者要生成中间聚合结果再哈希匹配,IO 和内存开销都更大。但注意:如果 OVER 子句里用了复杂 ORDER BY 或大范围窗口(如 ROWS BETWEEN 1000 PRECEDING AND 1000 FOLLOWING),排序成本会上升。
- MySQL 8.0+、PostgreSQL、SQL Server、Oracle 都支持标准窗口语法,但 SQLite 要 3.25+ 且部分功能受限
- 避免在
OVER中对非索引字段ORDER BY,否则可能触发文件排序(Using filesort) - 如果只是求分组总计(不需要排序逻辑),去掉
ORDER BY能显著提升性能
如何给窗口结果加条件过滤
不能在 WHERE 里直接引用窗口函数结果,因为 SQL 执行顺序中 WHERE 在窗口计算之前。正确做法是套一层子查询或 CTE,再在外层加条件。
- 错误写法:
WHERE SUM(amount) OVER(PARTITION BY user_id) > 1000→ 报错 “window function is not allowed here” - 正确写法:用 CTE
WITH t AS (SELECT *, SUM(amount) OVER(PARTITION BY user_id) AS total FROM orders) SELECT * FROM t WHERE total > 1000 - 如果只过滤某类分组(如“只看订单数超 5 的用户”),可在
PARTITION BY前先用WHERE预筛,减少窗口计算量
最易忽略的是执行计划里 WindowAgg 节点的实际输入行数——它反映窗口函数真正处理了多少行。有时候你以为只算一个分组,结果发现 PARTITION BY 字段有隐式 NULL 或类型不一致,导致分组崩掉,所有行被划进同一组。查前先 SELECT COUNT(*), COUNT(DISTINCT col) FROM table 对下底。











