runtime
76 / 88

lesson 76 Warehouse & reporting projects · advanced

Monthly change & cumulative reports

Measure monthly change and cumulative totals.

the idea

Monthly reports aggregate to one row per month first, then use window functions across months: SUM(…) OVER (ORDER BY month) for cumulative sales and LAG(…) for the previous month’s value and the percentage change.

Dataset: Retail

The tables the challenges use, with their first 6 rows. Under each column name: the kind of value it holds, and which table it links to.

fact_sales7 columns · 45,151 rows
sale_keynumber · unique idorder_idnumbercustomer_keynumber · links to dim_customerproduct_keynumber · links to dim_productorder_datedatequantitynumbersalesdecimal
65625367955620622011-01-04834
6562636795568812011-01-0417.95
6562736795568872011-01-0414.95
6562836795564012011-01-0425.9
65629367955640592011-01-043630.6
65630367955643012011-01-041029.5
sale_keynumber · unique id
65625
order_idnumber
3679
customer_keynumber · links to dim_customer
556
product_keynumber · links to dim_product
2062
order_datedate
2011-01-04
quantitynumber
8
salesdecimal
34
sale_keynumber · unique id
65626
order_idnumber
3679
customer_keynumber · links to dim_customer
556
product_keynumber · links to dim_product
881
order_datedate
2011-01-04
quantitynumber
1
salesdecimal
7.95
sale_keynumber · unique id
65627
order_idnumber
3679
customer_keynumber · links to dim_customer
556
product_keynumber · links to dim_product
887
order_datedate
2011-01-04
quantitynumber
1
salesdecimal
4.95
sale_keynumber · unique id
65628
order_idnumber
3679
customer_keynumber · links to dim_customer
556
product_keynumber · links to dim_product
401
order_datedate
2011-01-04
quantitynumber
2
salesdecimal
5.9
sale_keynumber · unique id
65629
order_idnumber
3679
customer_keynumber · links to dim_customer
556
product_keynumber · links to dim_product
4059
order_datedate
2011-01-04
quantitynumber
36
salesdecimal
30.6
sale_keynumber · unique id
65630
order_idnumber
3679
customer_keynumber · links to dim_customer
556
product_keynumber · links to dim_product
4301
order_datedate
2011-01-04
quantitynumber
10
salesdecimal
29.5

first 6 of 45,151 rows · your queries use all of them

worked example

Run these steps in order to see the idea before you write your own query. The step marked “the pattern” is what the challenges ask for. Running the example does not count toward progress.

  1. Aggregate sales by month first. One row per month with rounded totals—that monthly grain is what running sums and month-over-month windows need.the pattern

    example.sql
    SELECT substr(order_date, 1, 7) AS month, ROUND(SUM(sales), 2) AS sales
    FROM fact_sales
    GROUP BY month
    ORDER BY month;
  2. Find the busiest months by orders. Distinct order_id per month shows which months drove the most orders—November usually leads for this wholesaler.

    step-2.sql
    SELECT
      substr(order_date, 1, 7) AS month,
      COUNT(DISTINCT order_id) AS orders
    FROM fact_sales
    GROUP BY month
    ORDER BY orders DESC
    LIMIT 3;
  3. Compare each month to the prior one. After aggregating to months, LAG(orders) pulls the previous month’s value on the same result set.

    step-3.sql
    WITH m AS (
      SELECT
        substr(order_date, 1, 7) AS month,
        COUNT(DISTINCT order_id) AS orders
      FROM fact_sales
      GROUP BY month
    )
    SELECT
      month,
      orders,
      LAG(orders) OVER (ORDER BY month) AS previous_orders
    FROM m
    ORDER BY month;

common mistake

Applying LAG before aggregating compares sales lines, not months.

challenge 1

Return month (YYYY-MM), sales (rounded to 2) and running_sales (cumulative sales through that month, rounded to 2), ordered by month.

what to return

Columns
monthsalesrunning_sales in this order
Rows
in the order asked
challenge-1.sql
 
To master this lesson, solve both challenges without hints and pass the concept check. 3 of 3 to go.
Monthly change & cumulative reports — Warehouse & reporting projects · Runtime