使用日历表在日期范围内插值

时间:2015-02-24 15:22:57

标签: mysql

背景

我正在开发一个项目,我需要捕获某些id#的30天平均值,然后使用此平均值来确定某些新值是否异常。出于这个问题的目的,我们可以假设我只需要10天的平均值,因为解决方案可能类似。我目前有两个表:history,其中包含我按日记录的特定ID#号的实际值,但可以有一些缺失的日期和calendar日期表我30天平均需要的所有日子。

create table history (
   day      date   not null, 
   id       bigint not null, 
   category int    not null,
   value    int    not null default '0',
   primary key (day, id, category),
   key category (category)
);

create table calendar (
   day date not null primary key
);

我想获取历史记录表中的现有数据,并通过复制前一个值或复制前向值来填充缺失的数据。例如,在历史表中给出了这些数据:

+------------+-----------+----------+-------+
| day        | id        | category | value |
+------------+-----------+----------+-------+
| 2015-02-19 | 159253663 |    364   |   212 |
| 2015-02-20 | 159253663 |    364   |   211 |
| 2015-02-22 | 159253663 |    364   |   199 |
| 2015-02-23 | 159253663 |    364   |   192 |
| 2015-02-24 | 159253663 |    364   |   213 |
+------------+-----------+--------+---------+

注意:2015-02-21

没有条目

我想填写足够的数据,以便我可以计算10天的平均值,即将最旧的值(2015-02-19)复制回我的10天范围的开头,然后填写缺失的2015- 02-21值与前一天的值。结果就是这个(星号标记新添加的行):

+------------+-----------+----------+-------+
| day        | id        | category | value |
+------------+-----------+----------+-------+
| 2015-02-14 | 159253663 |    364   |   212 | *
| 2015-02-15 | 159253663 |    364   |   212 | *
| 2015-02-16 | 159253663 |    364   |   212 | *
| 2015-02-17 | 159253663 |    364   |   212 | *
| 2015-02-18 | 159253663 |    364   |   212 | *
| 2015-02-19 | 159253663 |    364   |   212 |
| 2015-02-20 | 159253663 |    364   |   211 |
| 2015-02-21 | 159253663 |    364   |   211 | *
| 2015-02-22 | 159253663 |    364   |   199 |
| 2015-02-23 | 159253663 |    364   |   192 |
| 2015-02-24 | 159253663 |    364   |   213 |
+------------+-----------+--------+---------+

ATTEMPT

我最初的想法是将连接添加到具有我需要的日期范围的日历表中,当我这样做时,我得到这样的结果:

select c.day, h.id, h.value
from calendar c 
   left join history h using (day)
where c.day between curdate() - interval 10 day and curdate();

+------------+-----------+----------+-----------+
| day        | id        | category |   value   |
+------------+-----------+----------+-----------+
| 2015-02-14 |      NULL |   NULL   |      NULL |
| 2015-02-15 |      NULL |   NULL   |      NULL |
| 2015-02-16 |      NULL |   NULL   |      NULL |
| 2015-02-17 |      NULL |   NULL   |      NULL |
| 2015-02-18 |      NULL |   NULL   |      NULL |
| 2015-02-19 | 159253663 |    364   |       212 |
| 2015-02-19 | 159253690 |    364   |       222 |
| 2015-02-20 | 159253663 |    364   |       211 |
| 2015-02-20 | 159253690 |    364   |       221 |
| 2015-02-21 |      NULL |   NULL   |      NULL |
| 2015-02-22 | 159253663 |    364   |       199 |
| 2015-02-22 | 159253690 |    364   |       209 |
| 2015-02-23 | 159253663 |    364   |       192 |
| 2015-02-23 | 159253690 |    364   |       202 |
| 2015-02-24 | 159253663 |    364   |       213 |
| 2015-02-24 | 159253690 |    364   |       213 |
+------------+-----------+----------+-----------+

我不确定从这一点开始的步骤,因为我需要为每个不同的id#每天输入一个条目。如果缺少此连接,则仅返回一天。我正在寻找更好的方法。我想在MySQL服务器上尽可能多地完成工作,但可以通过programmaticaly做一些事情。欢迎任何/所有想法或建议。

这是一个SQLFiddle,它具有我正在测试的DDL定义:http://sqlfiddle.com/#!2/cc206/2

1 个答案:

答案 0 :(得分:1)

以下使用@ variable和in-statement赋值来向后滚动值(和id):

SET @lastval = 0, @lastid = 0;
SELECT c.day, @lastid := COALESCE(h.id,@lastid) id,  @lastval := COALESCE(h.value,@lastval) VALUE, h.id id1,h.value v1
FROM (SELECT DISTINCT c.day,h.id FROM history h, calendar c) c
LEFT JOIN history h ON h.day = c.day AND h.id = c.id
WHERE c.day BETWEEN CURDATE() - INTERVAL 10 DAY AND CURDATE()
 ORDER BY COALESCE(h.id,@lastid),c.day DESC

子查询似乎是必要的,从来没有太确定为什么(有些人做,有些人不这样做)。

如果结果的顺序错误,您可能需要添加:

SET optimizer_switch='block_nested_loop=off';
在语句之前作为块嵌套循环优化可以搞乱mysql在收集行时使用的命令。