连续增长是指某字段在时间序列中逐期大于前一期且中间不断,不能用GROUP BY解决,因其无法识别逻辑连续性;必须用LAG()标记断点、SUM() OVER构造分组ID、再ROW_NUMBER()计算连续次数。

什么是连续增长,以及为什么不能用 GROUP BY
连续增长指的是某字段(比如销售额、用户数)在时间序列中逐期大于前一期,且中间不能断。很多人第一反应是用 GROUP BY 配合 LAG() 判断增减,再用分组聚合——但这样会把“连续段”打散:只要日期不连续或存在空值,GROUP BY 就无法识别逻辑上的连续性。
真正要的是“按时间排序后,对每个满足 value > LAG(value) 的记录打上相同标签,直到条件失败为止”,这必须依赖窗口函数的累积逻辑,而非静态分组。
用 SUM() OVER + CASE 实现连续计数
核心思路是构造一个“断点标记”:每当增长中断(即当前值 ≤ 前值),就产生一个新分组;然后对每个分组内行号计数。关键不是直接计数,而是先生成分组 ID,再用 ROW_NUMBER() 或 COUNT(*)。
- 先用
LAG()取前一行值,再用CASE WHEN value > LAG(value) THEN 0 ELSE 1 END标记是否为断点 - 用
SUM() OVER (ORDER BY date ROWS UNBOUNDED PRECEDING)累加断点,得到每个连续段唯一 ID - 最后套一层
ROW_NUMBER() OVER (PARTITION BY group_id ORDER BY date)即得连续增长次数
示例片段(PostgreSQL):
SELECT date, sales,
ROW_NUMBER() OVER (PARTITION BY grp ORDER BY date) AS streak
FROM (
SELECT date, sales,
SUM(is_break) OVER (ORDER BY date ROWS UNBOUNDED PRECEDING) AS grp
FROM (
SELECT date, sales,
CASE WHEN sales > LAG(sales) OVER (ORDER BY date)
THEN 0 ELSE 1 END AS is_break
FROM sales_table
) t1
) t2;
MySQL 8.0+ 和 SQL Server 的写法差异
MySQL 8.0+ 支持标准窗口函数,写法与 PostgreSQL 几乎一致,但注意 LAG() 默认返回 NULL,首次比较时需处理:sales > COALESCE(LAG(sales) OVER (ORDER BY date), 0) 更安全。
SQL Server 同样支持,但若用旧版本(如 2016),ROWS UNBOUNDED PRECEDING 可能报错,改用 RANGE UNBOUNDED PRECEDING(语义略有不同,但对 date 主键通常无影响)。
- SQLite 直到 3.25+ 才支持窗口函数,且不支持
RANGE,必须确保date无重复,否则ROWS可能误判顺序 - 如果时间字段有重复(比如同天多条记录),必须加入唯一排序列(如
id)避免LAG()取错行
容易被忽略的边界情况
连续增长计算最常出错的地方不在语法,而在数据本身:
-
NULL值:一旦sales为NULL,LAG()结果参与比较会导致整行被跳过或判断为中断,建议提前用COALESCE(sales, 0)或过滤掉 - 起始行:第一行没有前值,
LAG()返回NULL,sales > NULL恒为FALSE,会被当作中断起点——这是合理行为,但需确认业务是否把首行默认计入 streak=1 - 相等值:严格“增长”指大于,不是大于等于;若需求是“非递减”,则把
>改成>=,但要注意这会让平台期也被计入连续
实际跑之前,先用 SELECT *, LAG(sales) OVER (ORDER BY date) AS prev_sales 查一眼原始序列和前值是否对齐,比直接套公式更省调试时间。

















