sql时间序列统计核心是按业务节奏设计时间维度:先明确分组粒度(日/小时/周)、补全缺失日期、用LAG/LEAD做同比环比、加索引和分区提速。

SQL时间序列统计的核心是把时间字段当维度用,按需切片、聚合、对齐,而不是硬套复杂函数。关键不在“怎么写”,而在“怎么想”——先理清业务节奏(比如按天/小时滚动?是否要补全缺失日期?是否需同比环比?),再选对应策略。
时间分组要对齐业务粒度
别直接用 date(created_at) 或 YEAR(created_at) 就完事。得看业务真实需求:
- 想看“每天凌晨0点到次日0点”的销量?用 DATE(created_at) 最稳
- 要“每小时整点汇总”(如 9:00–10:00)?用 DATE_FORMAT(created_at, ‘%Y-%m-%d %H:00:00’) 或 CONCAT(DATE(created_at), ‘ ‘, HOUR(created_at), ‘:00:00’)
- 需按自然周(周一至周日)统计?用 DATE_SUB(created_at, INTERVAL WEEKDAY(created_at) DAY) 定位周一
缺失时间点必须主动补全
原始数据往往有空档(比如某天没订单),但报表常要求“连续日期轴”。纯 GROUP BY 会跳过空天,得自己造时间序列:
- mysql 8.0+:用 RECURSIVE CTE 生成日期表,再 LEFT JOIN 原表
- 低版本 MySQL / postgresql:建一张小的 dates 辅助表(含近3年所有日期),JOIN 时加条件限制范围
- 临时应急:用 union ALL 拼几段日期(适合固定短周期,如最近7天)
同比环比别硬算,用窗口函数降耦合
用 LAG() 和 LEAD() 直接拉同行数据,比自连接或子查询清晰又快:
- 环比(比上一天):LAG(sum_amount, 1) OVER (ORDER BY stat_date)
- 同比(比去年同天):LAG(sum_amount, 365) OVER (ORDER BY stat_date)(注意闰年偏差,生产建议用日期计算而非固定偏移)
- 带条件的同比(只比工作日):先用 CASE 标记工作日,再结合 LAG + WHERE 过滤,或用更精准的日期映射表
索引和分区是提速底线,不是可选项
没索引的时间字段查询=全表扫描,尤其大表:
- 高频按时间过滤?给 created_at 或 stat_date 加 B-Tree 索引
- 单表超千万行且按月/年归档?考虑 PARTITION BY RANGE (TO_DAYS(created_at))(MySQL)或按时间字段分区(PostgreSQL)
- 复合查询多?把时间字段放联合索引最左位,例如 (stat_date, status, user_id)
基本上就这些。不复杂但容易忽略——先定节奏、再补空档、善用窗口、最后靠索引兜底。跑得慢?八成卡在没补日期或没建索引。