SQL历史数据周期统计核心是构建合理时间窗口:自然周期用日期截断对齐起点,滚动窗口用RANGE BETWEEN实现动态范围,固定切片需日历表补零,跨周期对比用LAG/LEAD函数。

SQL中做历史数据的周期统计,核心是构建合理的时间窗口,而不是简单用GROUP BY加日期函数。关键在于明确业务周期(如自然月、滚动7天、财年季度等),再选择匹配的窗口函数或分组逻辑。
自然周期统计:用日期截断函数对齐业务单位
适用于按日/周/月/年汇总,比如“每月销售额”、“每周新增用户”。重点是把时间字段归一到周期起点,避免因时间精度导致分组错乱。
- PostgreSQL/MySQL 8.0+:用DATE_TRUNC('month', order_time) 或 YEAR(order_time), MONTH(order_time)
- MySQL 5.7:用DATE_FORMAT(order_time, '%Y-%m-01') 统一为当月1日
- 注意时区一致性——数据库时区、业务时区、前端展示时区需统一,否则凌晨订单可能被计入错误月份
滚动窗口统计:用窗口函数实现动态时间范围
适合计算“近30天日均访问量”、“过去7天累计转化率”这类动态指标。不能靠GROUP BY,必须依赖ROWS BETWEEN或RANGE BETWEEN定义滑动范围。
- 示例(近7天销售额滚动和):
SUM(sales) OVER (ORDER BY order_date RANGE BETWEEN INTERVAL '6 days' PRECEDING AND CURRENT ROW) - 注意:RANGE对日期更安全(自动处理无数据日期),ROWS按行数易跳过空日;大数据量下建议加order_date索引
- 滚动统计结果每行一个时间点,适合画趋势图,但不直接用于报表合计
固定周期切片:用CTE或日历表补全缺失时段
真实业务中常有某几天没数据,但报表要求“每天都要显示”,此时需主动构造完整周期,再LEFT JOIN补零。
- 推荐建一张轻量级dim_date日历表(含date、year_week、is_workday等字段)
- 或用递归CTE生成连续日期:WITH RECURSIVE dates AS (...)
- 再与原始数据LEFT JOIN,配合COALESCE(sales, 0)确保空日显示为0
跨周期对比:用LAG/LEAD实现同比环比
比较“本月 vs 上月”、“本周 vs 上周”本质是取同一周期在时间轴上的偏移值,窗口函数比自连接更高效稳定。
- 环比(较上一周期):LAG(monthly_sales, 1) OVER (ORDER BY stat_month)
- 同比(较上年同月):LAG(monthly_sales, 12) OVER (ORDER BY year, month)
- 注意:LAG默认返回NULL,需用COALESCE(..., 0)或CASE WHEN处理首期无对比值的情况










