背景
我正在开发一个项目,我需要捕获某些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
答案 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在收集行时使用的命令。