Calculate Moving Averages with Window Frames in SQL
📖 Scenario: You work for a retail company that tracks daily sales. You want to analyze sales trends by calculating moving averages of daily sales over a 3-day window.
🎯 Goal: Create a SQL query that calculates the 3-day moving average of daily sales using window frames.
📋 What You'll Learn
Create a table called daily_sales with columns sale_date (date) and amount (integer).
Insert the exact sales data for 5 consecutive days as specified.
Define a window frame that looks at the current day and the two previous days ordered by sale_date.
Calculate the moving average of amount over this window frame and name the result moving_avg.
💡 Why This Matters
🌍 Real World
Moving averages help businesses understand trends by smoothing out daily fluctuations in sales data.
💼 Career
Data analysts and database developers often use window functions to perform advanced calculations like moving averages for reporting and decision-making.
Progress0 / 4 steps
1
Create the daily_sales table and insert data
Create a table called daily_sales with columns sale_date of type DATE and amount of type INTEGER. Insert these exact rows into daily_sales: ('2024-01-01', 100), ('2024-01-02', 150), ('2024-01-03', 200), ('2024-01-04', 130), and ('2024-01-05', 170).
SQL
Hint
Use CREATE TABLE to define the table and INSERT INTO with multiple rows to add the sales data.
2
Define the window frame for the moving average
Write a SQL SELECT statement that selects sale_date and amount from daily_sales. Add a window frame clause that orders rows by sale_date and frames from 2 rows before the current row to the current row. Assign this window frame the name sales_window.
SQL
Hint
Use the WINDOW clause to define sales_window with the correct ordering and frame.
3
Calculate the 3-day moving average using the window frame
Modify the SELECT statement to include a new column called moving_avg that calculates the average of amount over the window frame sales_window using the AVG() function with OVER sales_window.
SQL
Hint
Use AVG(amount) OVER sales_window to calculate the moving average.
4
Complete the query with ordering by sale_date
Add an ORDER BY sale_date clause at the end of the SELECT statement to display the results in date order.
SQL
Hint
Use ORDER BY sale_date at the end of the query to sort the results by date.
Practice
(1/5)
1. What does the SQL clause ROWS BETWEEN 2 PRECEDING AND CURRENT ROW do in a window function?
easy
A. It calculates the function over the current row and the two rows before it.
B. It calculates the function over the current row and the next two rows.
C. It calculates the function over all rows in the table.
D. It calculates the function only on the current row.
Solution
Step 1: Understand the window frame boundaries
The clause ROWS BETWEEN 2 PRECEDING AND CURRENT ROW means the frame includes the current row and the two rows before it.
Step 2: Apply to window function calculation
The window function (like AVG) will compute its result using values from these three rows.
Final Answer:
It calculates the function over the current row and the two rows before it. -> Option A
Quick Check:
Window frame includes current + 2 preceding rows = C [OK]
Hint: Preceding means rows before current, including current [OK]
Common Mistakes:
Confusing PRECEDING with FOLLOWING
Thinking it includes future rows
Assuming it covers entire table
2. Which of the following is the correct syntax to calculate a 3-row moving average of the column sales ordered by date?
easy
A. SELECT date, AVG(sales) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data;
B. SELECT date, AVG(sales) OVER (PARTITION BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data;
C. SELECT date, AVG(sales) OVER (ORDER BY date ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS moving_avg FROM sales_data;
D. SELECT date, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data;
Solution
Step 1: Identify correct window function syntax
The correct syntax requires OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) to define ordering and frame.
Step 2: Check each option
SELECT date, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; uses ORDER BY with correct frame. SELECT date, AVG(sales) OVER (PARTITION BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; wrongly uses PARTITION BY instead of ORDER BY. SELECT date, AVG(sales) OVER (ORDER BY date ROWS BETWEEN CURRENT ROW AND 2 FOLLOWING) AS moving_avg FROM sales_data; uses FOLLOWING which looks ahead, not behind. SELECT date, AVG(sales) OVER (ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; misses ORDER BY clause.
Final Answer:
SELECT date, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; -> Option D
Quick Check:
ORDER BY + ROWS BETWEEN 2 PRECEDING AND CURRENT ROW = D [OK]
Hint: Use ORDER BY with ROWS BETWEEN for moving averages [OK]
Common Mistakes:
Using PARTITION BY instead of ORDER BY for moving average
Omitting ORDER BY clause
Using FOLLOWING instead of PRECEDING
3. Given the table sales_data with columns date and amount, what is the output of this query?
SELECT date, amount, AVG(amount) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data ORDER BY date;
Moving average includes current + 1 preceding row = A [OK]
Hint: Average current and previous row amounts for moving average [OK]
Common Mistakes:
Including future rows instead of preceding
Calculating average without window frame
Assuming null for first row moving average
4. Identify the error in this SQL query for calculating a moving average:
SELECT date, amount, AVG(amount) OVER (ORDER BY date ROWS BETWEEN CURRENT ROW AND 2 PRECEDING) AS moving_avg FROM sales_data;
medium
A. AVG() cannot be used as a window function.
B. The ORDER BY clause is missing.
C. The window frame boundaries are in the wrong order; PRECEDING must come before CURRENT ROW.
D. The query is correct and will run without errors.
Solution
Step 1: Check window frame syntax
The frame clause ROWS BETWEEN CURRENT ROW AND 2 PRECEDING is invalid because the start boundary must be before the end boundary.
Step 2: Correct the order of boundaries
The correct syntax is ROWS BETWEEN 2 PRECEDING AND CURRENT ROW, with the smaller offset first.
Final Answer:
The window frame boundaries are in the wrong order; PRECEDING must come before CURRENT ROW. -> Option C
Quick Check:
Window frame start must be before end = A [OK]
Hint: Start frame boundary must be before end boundary [OK]
Common Mistakes:
Reversing PRECEDING and CURRENT ROW order
Thinking AVG() can't be window function
Ignoring window frame syntax rules
5. You want to calculate a 5-day moving average of daily sales, but only include days where sales were above 100. Which SQL query correctly applies this condition inside the moving average calculation?
hard
A. SELECT date, sales, AVG(CASE WHEN sales > 100 THEN sales ELSE NULL END) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data;
B. SELECT date, sales, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) WHERE sales > 100 FROM sales_data;
C. SELECT date, sales, AVG(sales) FILTER (WHERE sales > 100) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data;
D. SELECT date, sales, AVG(sales) OVER (PARTITION BY sales > 100 ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data;
To include only sales > 100 in the average, use a CASE expression inside AVG to nullify unwanted rows.
Step 2: Evaluate each option
SELECT date, sales, AVG(CASE WHEN sales > 100 THEN sales ELSE NULL END) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; uses CASE to include sales > 100, replacing others with NULL (ignored by AVG). SELECT date, sales, AVG(sales) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) WHERE sales > 100 FROM sales_data; filters rows after window function (invalid syntax). SELECT date, sales, AVG(sales) FILTER (WHERE sales > 100) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; uses FILTER clause which is not standard in all SQL versions. SELECT date, sales, AVG(sales) OVER (PARTITION BY sales > 100 ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; partitions by boolean condition, which changes grouping incorrectly.
Final Answer:
SELECT date, sales, AVG(CASE WHEN sales > 100 THEN sales ELSE NULL END) OVER (ORDER BY date ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) AS moving_avg FROM sales_data; -> Option A
Quick Check:
Use CASE inside AVG to filter rows in window [OK]
Hint: Use CASE inside AVG to filter values in window frame [OK]
Common Mistakes:
Filtering rows outside window function
Using PARTITION BY for filtering condition
Assuming FILTER clause always works
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.