Moving Average

April 6, 2018

Moving Average

Suppose you are the owner of a chain of burger outlets, and you have the dataset of customer ratings over the past six months for each store. How then, will you determine which outlets are improving against those that are not?

Calculating the moving average for each outlet is one way this can be achieved.

We will be using the classicmodels database, which you can download from mysqltutorial for this article.


The Order Details Table


SELECT
   orderNumber,
   productCode,
   quantityOrdered,
   priceEach 
FROM
   orderdetails 
WHERE
   orderNumber IN 
   (
      10100,
      10101
   )
;
orderNumber productCode quantityOrdered priceEach
10100 S18_1749 30 136
10100 S18_2248 50 55.09
10100 S18_4409 22 75.46
10100 S24_3969 49 35.29
10101 S18_2325 25 108.06
10101 S18_2795 26 167.06
10101 S24_1937 45 32.53
10101 S24_2022 46 44.35

We shall assume that each order was made chronologically, and we want to determine if the order amounts are increasing or decreasing over time. We first have to create an intermediate table where we calculate the amount for each order.


Order Totals


SELECT
   lineTotals.orderNumber,
   SUM(lineTotals.lineTotal) AS 'orderTotal' 
FROM
   (
      SELECT
         orderNumber,
         quantityOrdered * priceEach AS 'lineTotal' 
      FROM
         orderdetails
   )
   AS lineTotals 
GROUP BY
   lineTotals.orderNumber 
ORDER BY
   lineTotals.orderNumber LIMIT 20;
orderNumber orderTotal
10100 10223.83
10101 10549.01
10102 5494.78
10103 50218.95
10104 40206.2
10105 53959.21
10106 52151.81
10107 22292.62
10108 51001.22
10109 25833.14
10110 48425.69
10111 16537.85
10112 7674.94
10113 11044.3
10114 33383.14
10115 21665.98
10116 1627.56
10117 44380.15
10118 3101.4
10119 35826.33


Moving Average


SELECT
   ordertotals.ordernumber,
   ordertotals.ordertotal,
   AVG(ordertotals.ordertotal) OVER( 
ORDER BY
   ordertotals.ordernumber ASC range BETWEEN 4 PRECEDING AND 0 FOLLOWING) AS 'maFive' 
FROM
   (
      SELECT
         linetotals.ordernumber,
         sum(linetotals.linetotal) AS 'orderTotal' 
      FROM
         (
            SELECT
               ordernumber,
               quantityordered * priceeach AS 'lineTotal' 
            FROM
               orderdetails 
         )
         AS linetotals 
      GROUP BY
         linetotals.ordernumber 
      ORDER BY
         linetotals.ordernumber LIMIT 20 
   )
   ordertotals;
orderNumber orderTotal maFive
10100 10223.83 10223.83
10101 10549.01 10386.42
10102 5494.78 8755.873333
10103 50218.95 19121.6425
10104 40206.2 23338.554
10105 53959.21 32085.63
10106 52151.81 40406.19
10107 22292.62 43765.758
10108 51001.22 43922.212
10109 25833.14 41047.6
10110 48425.69 39940.896
10111 16537.85 32818.104
10112 7674.94 29894.568
10113 11044.3 21903.184
10114 33383.14 23413.184
10115 21665.98 18061.242
10116 1627.56 15079.184
10117 44380.15 22420.226
10118 3101.4 20831.646
10119 35826.33 21320.284

There are two points that you should note about the query and result set.

  1. The BETWEEN function includes the edges. Therefore, if we are calculating the moving average of the last 5 entries, we intend to include the current entry as well as the past four entries, not five.

  2. SQL will calculate the moving average even when there are insufficient entries (note the moving average in the first four rows). Do note and make necessary adjustments if this is not required for your analysis.

    This query was performed in MySQL - your results might vary based on the dialect of SQL that you are using.

comments powered by Disqus