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.
Calculate the 5-day moving average - Set value to 0 if there is a gap between dates.
-
30-05-2022 - |
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";
Solution
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