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.
- 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. 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.