Question

I'm trying to calculate the 5-day moving average. And everything works fine, except when there is a gap between dates. When there is a gap, the value for the missing date(-s) should be set to zero to show the correct moving average.

Here is my table (orders_total) and you can see that there is no date for 1/7/13 and that is causing the problem:

orders_id    value    date
1            199      1/1/13 0:00
2            199      1/2/13 0:00
3            199      1/3/13 0:00
4            199      1/4/13 0:00
5            249      1/5/13 0:00
6            199      1/6/13 0:00
7            199      1/8/13 0:00
8            199      1/9/13 0:00
9            199      1/10/13 0:00
10           199      1/11/13 0:00
11           199      1/12/13 0:00
12           199      1/13/13 0:00

If the value for missing date 1/7/13 is set to zero, the correct 5-day moving average (that 'm looking for) is:

199
199
199
199
209
209
169.2
169.2
169.2
159.2
159.2
199
199

This is the code I'm using, and it is not showing the correct moving average when there is a gap between dates:

    SELECT ot1.value, ot1.date,
    (SELECT SUM(ot2.value) / COUNT(ot2.value)
    FROM orders_total AS ot2 WHERE DATEDIFF(ot1.date, ot2.date) BETWEEN 0 AND 4) AS '5dayMovingAvg'
    FROM orders_total AS ot1 ORDER BY ot1.date";
Was it helpful?

Solution

If you know that it will always be 5 days, why divide by COUNT(ot2.value)? Unless you're trying to handle edge cases, in which case, handle the edge cases differently than the normal case.

OTHER TIPS

Try this query:

SELECT ot1.value, ot1.date,
            (    SELECT SUM(ot2.value) / COUNT(ot2.value)
            FROM (

     select * from orders_total union
        (SELECT 0 as order_id,0 as `value`,DATE(r1.date) + INTERVAL 1 DAY AS missing_date
        FROM orders_total r1
        LEFT OUTER JOIN orders_total r2 ON DATE(r1.date) = DATE(r2.date) - INTERVAL 1 DAY 
        WHERE r2.date IS NULL )

) AS ot2 WHERE DATEDIFF(ot1.date, ot2.date) BETWEEN 0 AND 4) AS '5dayMovingAvg'
            FROM (

    select * from orders_total union
        (SELECT 0 as order_id,0 as `value`,DATE(r1.date) + INTERVAL 1 DAY AS missing_date
        FROM orders_total r1
        LEFT OUTER JOIN orders_total r2 ON DATE(r1.date) = DATE(r2.date) - INTERVAL 1 DAY 
        WHERE r2.date IS NULL )

) 

        AS ot1 ORDER BY ot1.date

Basically what I have done is I have replaced orders_total tabel from your query and replaced with

select * from orders_total union
        (SELECT 0 as order_id,0 as `value`,DATE(r1.date) + INTERVAL 1 DAY AS missing_date
        FROM orders_total r1
        LEFT OUTER JOIN orders_total r2 ON DATE(r1.date) = DATE(r2.date) - INTERVAL 1 DAY 
        WHERE r2.date IS NULL )

this query , I could have placed it in view but mysql view doesn't support union in view probably a bug http://bugs.mysql.com/bug.php?id=9198

note:this will also include one extra row for date you can exclude it with max(date)

result from above query:

199 2013-01-01 00:00:00 199.0000
199 2013-01-02 00:00:00 199.0000
199 2013-01-03 00:00:00 199.0000
199 2013-01-04 00:00:00 199.0000
249 2013-01-05 00:00:00 209.0000
199 2013-01-06 00:00:00 209.0000
0   2013-01-07 00:00:00 169.2000
199 2013-01-08 00:00:00 169.2000
199 2013-01-09 00:00:00 169.2000
199 2013-01-10 00:00:00 159.2000
199 2013-01-11 00:00:00 159.2000
199 2013-01-12 00:00:00 199.0000
199 2013-01-13 00:00:00 199.0000
0   2013-01-14 00:00:00 159.2000
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top