我想创建一个时间线报告,针对时间线中的每个日期显示数据集中最新 N 个数据点的移动平均值,该数据集中具有一些度量值和测量日期。我有一个包含每天的日历表来提供日期。我可以计算一个时间线,用相关子查询相当简单地显示该日期之前的总体平均值(实际情况比这复杂得多,但它基本上可以简化为这样):
SELECT c.date
, ( SELECT AVERAGE(m.value)
FROM measures as m
WHERE m.measured_on_dt <= c.date
) as `average_to_date`
FROM calendar c
WHERE c.date between date1 AND date2 -- graph boundaries
ORDER BY c.date ASC
我花了几天时间阅读这个问题,但没有找到任何好的解决方案。有人建议 LIMIT 可能在子查询中工作(当前版本的 MySQL 在子查询中支持 LIMIT),但是 LIMIT 适用于返回集,而不是进入聚合的行,因此添加它没有区别。
我也不能编写带有 LIMIT 的非聚合 SELECT 然后聚合它,因为在 FROM 语句中不允许关联子查询。所以这(可悲)不起作用:
SELECT c.date
, SELECT AVERAGE(last_5.value)
FROM ( SELECT m.value
FROM measures as m
WHERE m.measured_on_dt <= c.date
ORDER BY m.measured_on_dt DESC
LIMIT 5
) as `last_5`
FROM calendar c
WHERE c.date between date1 AND date2 -- graph boundaries
ORDER BY c.date ASC
我在想我需要完全避免使用子查询方法,看看我是否使用巧妙的连接/行编号技术和用户变量来做到这一点,然后聚合它,但是当我正在处理这个问题时,我想我会问是否有人知道更好的方法吗?
更新:好的,我已经为这个例子简化了一个解决方案。它依赖于一些用户变量的技巧来从日历日期向后编号度量。它还与日历表(而不是子查询)进行交叉乘积,但这具有导致行编号技巧失败的不幸副作用(用户变量在发送到客户端时进行评估,而不是在行被评估)所以为了解决这个问题,我不得不将查询嵌套一层,对结果进行排序,然后将行编号技巧应用于该集合,然后它就可以工作了。
此查询仅返回有度量的日历日期,因此如果您想要整个时间线,您只需选择日历并 LEFT JOIN 到此结果集。
set @day = 0;
set @num = 0;
set @LIMIT = 5;
SELECT date
, AVG(value) as recent_N_AVG
FROM
( SELECT *
, @num := if(@day = c.date, @num + 1, 1) as day_row_number
, @day := day as dummy
FROM
( SELECT c.full_date
, m.value
, m.measured_on_dt
FROM calendar c
JOIN measures as m
WHERE m.measured_on_dt <= c.full_date
AND c.full_date BETWEEN date1 AND date2
ORDER BY c.full_date ASC, measured_on_dt DESC
) as full_data
) as numbered
WHERE day_row_number <= @LIMIT
GROUP BY date
行编号技巧可以推广到更复杂的数据(我的度量是在需要汇总的多个维度中)。