关键前提是将日期划分为连续组:对排序后日期生成行号,用日期减行号,结果相同即属同一连续段;该差值即为分组锚点,可据此聚合起止日期与天数。

识别连续日期段是关键前提
直接用 GROUP BY date 只能得到每日统计,无法合并连续日期。必须先将原始日期划分为“连续组”——同一组内日期差值恒为1,跨组则出现断点。核心思路是:对排序后的日期生成行号,再用日期减去行号,结果相同的即属于同一连续段。
用窗口函数构造连续分组标识
在支持 ROW_NUMBER() 的数据库(如 PostgreSQL、SQL Server、MySQL 8.0+)中,按日期升序编号,再计算 date - INTERVAL ROW_NUMBER() OVER (ORDER BY date) DAY(MySQL/PostgreSQL)或 DATEADD(day, -ROW_NUMBER() OVER (ORDER BY date), date)(SQL Server)。该表达式结果就是每段连续区间的“锚点日期”,可作为分组依据。
示例(PostgreSQL):
SELECT
MIN(date) AS start_date,
MAX(date) AS end_date,
COUNT(*) AS total_days,
SUM(order_count) AS total_orders
FROM (
SELECT
date,
order_count,
date - INTERVAL '1 day' * ROW_NUMBER() OVER (ORDER BY date) AS grp
FROM daily_orders
) t
GROUP BY grp
ORDER BY start_date;MySQL 5.7 或不支持窗口函数时的替代方案
需用变量模拟行号,但必须确保查询顺序严格可控(加 ORDER BY + 禁用优化器重排)。常见错误是变量未初始化或执行顺序不可靠,导致 @row := @row + 1 错位。
- 必须显式初始化变量:
SELECT @row := 0在同一会话先执行 - 子查询中强制排序:
(SELECT @row := @row + 1 AS rn, date FROM daily_orders ORDER BY date) - 日期运算改用
DATE_SUB(date, INTERVAL @row DAY)得到分组键 - 注意:该写法在 MySQL 8.0+ 应优先用
ROW_NUMBER(),变量方案仅作兼容兜底
性能与边界情况提醒
连续日期统计本质是 O(n log n) 排序操作,数据量大时务必在 date 字段建索引。另外要注意:
- 日期字段是否含时间部分?建议先用
DATE(date_col)归一化 - 是否存在重复日期?应先聚合去重(如
SUM(order_count) GROUP BY date)再做连续分析 - 跨年、闰日等不影响算法,但若用字符串日期(如 '20230101'),需转为日期类型再计算,否则减法无意义
最易被忽略的是:没有预处理空缺日期——这个统计只针对“已有记录的连续日期”,不会自动补全中间缺失日。如果业务需要“补零后找最长连续”,得先生成完整日期序列再 LEFT JOIN。

















