How to do a moving average on a range of dates in SQL?

后端 未结 2 1618
Happy的楠姐
Happy的楠姐 2020-12-29 16:14

Let\'s just say I have two columns, DATE and VALUE. The dates are not necessarily regularly spaced intervals. How can I create a moving average on VALUE over the past N days

相关标签:
2条回答
  • 2020-12-29 16:59

    The MySQL example below covers a sliding 7-day window:

    select t1.`DATE`, AVG(t2.`VALUE`) as MV_AVG
    from MyTable t1
    left outer join MyTable t2 
        on t2.`DATE` between DATE_ADD(t1.`DATE`, INTERVAL -6 DAY) 
            and t1.`DATE`
    group by t1.`DATE`
    

    SQL Fiddle Example

    Output:

    |                             DATE |    MV_AVG |
    ------------------------------------------------
    |    August, 12 2012 20:00:00+0000 |       160 |
    |    August, 19 2012 20:00:00+0000 |        52 |
    |    August, 26 2012 20:00:00+0000 |        63 |
    | September, 03 2012 20:00:00+0000 |        41 |
    | September, 09 2012 20:00:00+0000 |      30.5 |
    | September, 16 2012 20:00:00+0000 |        20 |
    | September, 23 2012 20:00:00+0000 |       285 |
    | September, 24 2012 20:00:00+0000 |     152.5 |
    | September, 30 2012 20:00:00+0000 |      52.5 |
    |   October, 08 2012 20:00:00+0000 |        41 |
    |   October, 14 2012 20:00:00+0000 |      6037 |
    |   October, 15 2012 20:00:00+0000 |      6610 |
    |   October, 16 2012 20:00:00+0000 | 5624.6667 |
    |   October, 21 2012 20:00:00+0000 | 1649.6667 |
    |   October, 28 2012 20:00:00+0000 |        31 |
    |  November, 04 2012 19:00:00+0000 |        10 |
    
    0 讨论(0)
  • 2020-12-29 17:05

    RedFilter's excellent answer uses non-evenly spaced buckets. If you want evenly spaced buckets do something like this:

    SELECT
    DATE_FORMAT(CURDATE() - INTERVAL ((i.id - 0) * 7) - 1 DAY, '%Y-%m-%d') AS start,
    DATE_FORMAT(CURDATE() - INTERVAL ((i.id - 1) * 7) DAY, '%Y-%m-%d') AS end,
    AVG(d.value) average
    FROM
    ids i
    LEFT OUTER JOIN
    dates d ON
    d.date BETWEEN 
      (CURDATE() - INTERVAL ((i.id - 0) * 7) - 1 DAY) AND
      (CURDATE() - INTERVAL ((i.id - 1) * 7) DAY)
    WHERE
    i.id BETWEEN 0 AND 20
    GROUP BY
    1
    ORDER BY
    1 DESC;
    

    which provides this:

    +------------+------------+-----------+
    | start      | end        | average   |
    +------------+------------+-----------+
    | 2012-11-01 | 2012-11-07 |   10.0000 |
    | 2012-10-25 | 2012-10-31 |   31.0000 |
    | 2012-10-18 | 2012-10-24 |  108.0000 |
    | 2012-10-11 | 2012-10-17 | 5624.6667 |
    | 2012-10-04 | 2012-10-10 |   41.0000 |
    | 2012-09-27 | 2012-10-03 |   85.0000 |
    | 2012-09-20 | 2012-09-26 |  152.5000 |
    | 2012-09-13 | 2012-09-19 |   20.0000 |
    | 2012-09-06 | 2012-09-12 |   20.0000 |
    | 2012-08-30 | 2012-09-05 |   41.0000 |
    | 2012-08-23 | 2012-08-29 |   63.0000 |
    | 2012-08-16 | 2012-08-22 |   52.0000 |
    | 2012-08-09 | 2012-08-15 |  160.0000 |
    | 2012-08-02 | 2012-08-08 |      NULL |
    | 2012-07-26 | 2012-08-01 |      NULL |
    | 2012-07-19 | 2012-07-25 |      NULL |
    | 2012-07-12 | 2012-07-18 |      NULL |
    | 2012-07-05 | 2012-07-11 |      NULL |
    | 2012-06-28 | 2012-07-04 |      NULL |
    | 2012-06-21 | 2012-06-27 |      NULL |
    +------------+------------+-----------+
    20 rows in set (0.00 sec)
    

    See http://sqlfiddle.com/#!2/78c52/39 for a working example without CURDATE().

    0 讨论(0)
提交回复
热议问题