在关系数据库中处理时间序列的问题出现在分析和经典 SQL 编程的交叉点上。在 SQL-92 中没有特殊的窗口函数,因此必须通过子查询来计算动态指标(如滑动总和、平均值等)和时间条件。
问题 — 缺乏标准工具来进行滑动窗口的聚合、按时间查找前一个/下一个值、根据任意日历间隔(例如计算周/月指标)进行有效分组。
解决方案:
仅使用标准工具,通过对每一行或按计算标准(例如月份、周)进行分组的相关子查询:
代码示例:
-- 无窗口函数的周分组示例 SELECT YEAR(event_date) AS year, WEEK(event_date) AS week, SUM(value) AS total FROM timeseries GROUP BY YEAR(event_date), WEEK(event_date) ORDER BY year, week; -- 查找先前记录的相关子查询 SELECT t1.id, t1.event_date, t1.value, ( SELECT t2.value FROM timeseries t2 WHERE t2.event_date < t1.event_date ORDER BY t2.event_date DESC LIMIT 1 ) as prev_value FROM timeseries t1;
关键特点:
WEEK(event_date) 是否始终明确确定任何日期的日历周?
否——不同的数据库(甚至同一数据库的不同参数)对年份的第一周有不同的定义(例如,ISO 8601 与美国系统),这可能在聚合时导致不同结果。需要明确指定函数的工作模式或使用 YEARWEEK。
SELECT YEARWEEK(event_date, 1) -- 1: ISO 周以周一开始 FROM timeseries;
相关子查询在查找前一个值时是否自动删除重复项?
否,相关子查询默认不过滤重复项。如果表中某一日期有多个事件——子查询将返回按排序的第一个,但忽略其他。
可以通过 GROUP BY 对日期进行聚合而不考虑时间吗?
可以,但需要明确去掉时间部分,例如,通过 DATE(event_date) 或 TRUNC(event_date) 在不同数据库中:
SELECT DATE(event_datetime), COUNT(*) FROM events GROUP BY DATE(event_datetime)
团队通过 WEEK(date) 函数进行周分析,而没有将参数设置为 ISO-8601。结果,1 月的第一周报告“丢失”——部分事件根据美国逻辑被归类为去年 12 月,分析结果不一致。
优点:
缺点:
专家实施了 YEARWEEK(date, 1) 和日历表,显著提高了国家和业务部门之间的报告一致性。
优点:
缺点: