2

我想创建一个时间线报告,针对时间线中的每个日期显示数据集中最新 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

行编号技巧可以推广到更复杂的数据(我的度量是在需要汇总的多个维度中)。

4

1 回答 1

0

如果您的时间线是连续的(每天 1 个值),您可以像这样改进您的第一次尝试:

SELECT c.date,
       ( SELECT AVERAGE(m.value) 
         FROM   measures as m
         WHERE  m.measured_on_dt 
                    BETWEEN DATE_SUB(c.date, INTERVAL 5 day) AND c.date
       ) as `average_to_date`
FROM    calendar c
WHERE   c.date between date1 AND date2  -- graph boundaries
ORDER BY c.date ASC

如果您的时间线中有漏洞,这将导致平均值少于 5 个。

于 2012-04-12T11:22:05.330 回答