MAX()无法直接统计“增长值”因其仅聚合现有列,不生成相邻行差值;须先用LAG()或自连接计算每行增幅,再对外层结果聚合。

为什么 MAX() 单独用在分组里统计不了“增长值”
因为增长值是相邻行之间的差值(比如本月值减上月值),而 MAX() 只能对已存在的列做聚合,它不生成新值,也不感知行序。直接写 SELECT group_id, MAX(value) FROM t GROUP BY group_id 得到的是每组最大原始值,不是最大增幅。
常见错误现象:用 LAG() 算出增幅后,再套一层 GROUP BY + MAX(incr),结果报错或返回 NULL——这是因为窗口函数不能嵌套在聚合函数里直接使用,且 LAG() 必须在窗口定义后才能计算。
- 必须先用窗口函数算出每行的“相对于前一行的增长值”,再对这个临时列做聚合
- 若数据无明确时间/顺序字段,
LAG()会按任意顺序取“前一行”,结果不可靠 - 首行的
LAG()默认返回NULL,需用COALESCE(incr, 0)避免MAX()跳过整组
用 ROW_NUMBER() + 自连接模拟增长计算(兼容 MySQL 5.7 / SQL Server 2012)
当数据库不支持 LAG()(如旧版 MySQL),就得靠自连接+排序序号来配对相邻行。核心思路是给每组内数据按时间戳打序号,然后让 r1.seq = r2.seq + 1 实现“当前行 vs 上一行”关联。
示例(假设表 sales 有 region, month, amount):
SELECT r1.region, MAX(r1.amount - r2.amount) AS max_growth
FROM (
SELECT region, month, amount,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY month) AS seq
FROM sales
) r1
JOIN (
SELECT region, month, amount,
ROW_NUMBER() OVER (PARTITION BY region ORDER BY month) AS seq
FROM sales
) r2 ON r1.region = r2.region AND r1.seq = r2.seq + 1
GROUP BY r1.region;注意点:
-
ORDER BY month必须严格,否则序号错位,增长值就乱了 - 自连接使数据量翻倍,大表慎用;加
WHERE r2.amount IS NOT NULL无法优化,因为r2是子查询别名 - 如果某组只有 1 条记录,该组不会出现在结果中(因无“上一行”可联),需额外补全
LAG() + MAX() 的标准写法(PostgreSQL / MySQL 8.0+ / SQL Server 2016+)
这是最简洁可靠的路径:先用 LAG() 算出每行的增长值,再用外层聚合取最大。关键在于两层查询分离——窗口计算在内层,聚合在外层。
SELECT region, MAX(growth) AS max_growth
FROM (
SELECT region,
amount - LAG(amount) OVER (
PARTITION BY region ORDER BY month
) AS growth
FROM sales
) t
GROUP BY region;参数差异与陷阱:
-
LAG(amount, 1, 0)第三个参数设默认值,可避免首行growth为NULL导致MAX()返回NULL - 若
month有重复值,ORDER BY month不足保证唯一顺序,应追加主键或ORDER BY month, id - 某些方言(如旧版 Presto)要求
LAG()的ORDER BY表达式必须出现在SELECT列表中,此时需显式写出month
性能敏感时,为什么不该在 WHERE 中提前过滤再算增长
比如想只看“2023 年之后”的最大增长,写成 WHERE month > '2023-01-01' 再套窗口函数,会导致首行丢失参照基准——2023-02 的增长值需要 2023-01 的数据,但后者被过滤掉了。
正确做法是:窗口计算必须基于完整有序序列,过滤放最外层或用条件聚合:
SELECT region, MAX(CASE WHEN month > '2023-01-01' THEN growth END) AS max_growth_2023
FROM (
SELECT region, month,
amount - LAG(amount) OVER (PARTITION BY region ORDER BY month) AS growth
FROM sales
) t
GROUP BY region;容易被忽略的地方:增长值本身依赖上下文完整性,任何中间层的 WHERE 或 LIMIT 都可能切断行间依赖链。窗口函数的执行顺序永远早于外部聚合和过滤。

















