编程SQL 分析师

如何在仅限于标准 SQL-92 的情况下有效地对时间序列数据进行过滤和聚合?

用 Hintsage AI 助手通过面试

回答

在关系数据库中处理时间序列的问题出现在分析和经典 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;

关键特点:

  • 需要明确按时间函数(YEAR、MONTH、WEEK 等)进行分组和聚合。
  • 为了获得滑动计算,需要使用相关子查询或临时表。
  • 在处理大量数据时,性能受到影响——对每一行的子查询会迅速降低执行速度。

具有陷阱的问题。

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)

常见错误和反模式

  • 使用 YEAR/MONTH/WEEK 而不考虑本地化和报告周期的日历标准
  • 为滑动窗口创建过于复杂的相关子查询——在处理大量数据时执行时间急剧上升
  • 在操作时间函数时不考虑时区

生活中的例子

负面案例

团队通过 WEEK(date) 函数进行周分析,而没有将参数设置为 ISO-8601。结果,1 月的第一周报告“丢失”——部分事件根据美国逻辑被归类为去年 12 月,分析结果不一致。

优点:

  • 快速实施了周期的分解

缺点:

  • 数据不正确,报告与业务逻辑不符

积极案例

专家实施了 YEARWEEK(date, 1) 和日历表,显著提高了国家和业务部门之间的报告一致性。

优点:

  • 业务始终获得正确的周和月份编号

缺点:

  • 维护稍复杂——需要更新日历表