MySQL 8.0+原生支持SUM() OVER()等窗口函数,低版本需变量模拟但易出错且性能差;必须显式写ORDER BY和ROWS BETWEEN子句才能实现逐行累计,NULL值需COALESCE处理,大数据量须建PARTITION BY与ORDER BY联合索引。

MySQL 8.0+ 原生支持 Oracle 风格的窗口聚合函数(如 SUM() OVER()、AVG() OVER()),无需变量模拟;但若版本低于 8.0.2,或需特殊逻辑(如带条件累计、跳过某类值),才考虑变量方案——而变量方式极易出错,且性能差。
确认你真需要“类似Oracle的聚合函数”
先区分清楚:你要的是「分组后保留每行并加聚合结果」(即窗口行为),还是「自定义聚合逻辑」(如加权平均、中位数、去重计数)?
- 前者是标准窗口函数场景,
SUM(amount) OVER (PARTITION BY dept ORDER BY date)直接可用 - 后者才是自定义聚合函数范畴,必须用 C/C++ 编写共享库注册,不能靠 SQL 变量实现
- 用变量拼凑
SUM() OVER()效果(比如手动累加)属于高危操作:执行顺序不保证、并发下错乱、无法利用索引
MySQL 8.0+ 正确使用 SUM() OVER() 等窗口聚合函数
语法看着简单,但踩坑点集中在 frame 定义和 NULL 处理:
-
OVER()内必须有ORDER BY,否则默认 frame 是RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING(整分区),不是“逐行累计” - 要累计和,得显式写:
SUM(amount) OVER (PARTITION BY user_id ORDER BY create_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - MySQL 8.0.33 前不支持
NULLS FIRST,若排序字段含 NULL 且需排最前,用ORDER BY col IS NULL DESC, col - 别在
WHERE或HAVING中直接引用窗口函数别名,必须套子查询或 CTE
MySQL 5.7 及更早:变量模拟窗口聚合的致命限制
变量方式本质是逐行计算,无法真正复现窗口语义,尤其在以下情况必然失败:
- JOIN 后再排序编号:优化器可能重排执行顺序,
@sum := @sum + amount在 JOIN 结果集上执行,顺序不可控 - 没有主键/唯一排序依据时,
ORDER BY子句可能被优化器忽略,导致累计值错乱 - 无法表达滑动窗口(如“最近 7 天销售额”),只能做从头到尾的累计
- EXPLAIN 显示
Using filesort和Using temporary,百万级数据下比窗口函数慢 10 倍以上
自定义聚合函数不是“SQL 写法问题”,而是 C 扩展工程
如果你真需要 Oracle 的 STATS_MODE() 或自定义加权聚合,变量和窗口函数都无能为力:
- 必须用 C 编写五个函数:
xxx_init、xxx_add、xxx_clear、xxx_remove、xxx_deinit - 编译成
.so文件后,放入plugin_dir(查SHOW VARIABLES LIKE 'plugin_dir') - 用
CREATE AGGREGATE FUNCTION my_avg RETURNS REAL SONAME 'my_avg.so'注册 - 注册后才能在
SELECT中像普通聚合函数一样用:SELECT my_avg(score) FROM exam GROUP BY class
真正容易被忽略的是:自定义聚合函数不支持窗口用法(即不能写 my_avg(score) OVER ()),它仍是传统 GROUP BY 聚合。想在窗口里用自定义逻辑,目前 MySQL 还做不到。


















