The Slow Build

A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
medium
Seniority
L4
Asked in
Audible

Problem

Each row of transactions is one sale with its total_amount and transaction_date, and a month's revenue is the sum of its sales, refunds included. List every month of 2026 from the first onward with its revenue next to the average monthly revenue of all months so far, that month included, as a whole-dollar integer with the cents dropped.

Month over month, the number grows. Track how the average moves with it.

Tables

transactions

Columns: transaction_id INTEGER, user_id INTEGER, product_id INTEGER, quantity INTEGER, total_amount REAL, transaction_date TEXT

transactions: 10 of 200 rows
transaction_iduser_idproduct_idquantitytotal_amounttransaction_date
10671971001223.462026-02-02
11342941050336.932026-03-03
12013911099450.42026-04-04
12684881148563.872026-05-05
13355851197002026-06-06
14026821246290.812026-07-07
146977912953104.282026-08-08
153687613444117.752026-09-09
160397313935-452026-01-10
1670107014421144.692026-02-11

products

Columns: product_id INTEGER, product_name TEXT, category TEXT, price REAL, rating REAL, in_stock INTEGER

products: 10 of 200 rows
product_idproduct_namecategorypriceratingin_stock
1001Basic Device 1XBooks17.522.71
1050Deluxe Bundle 2XClothing25.054.41
1099Pro Unit 3XHome & Kitchen32.582.11
1148Ultra Tool 4XCollectibles40.113.81
1197Essential Set 5XToys47.641.51
1246Classic System 6XBeauty03.21
1295Smart Gadget 7XAutomotive62.74.90
1344Eco Kit 8XGarden70.232.61
1393Turbo Pack 9XMusic77.764.30
1442Premium Widget 10XElectronics85.2921

Expected output

First 5 of 9 rows
monthmonthly_revenuecumulative_avg
2026-0110187.3710187
2026-029447.639817
2026-038508.219381
2026-048925.789267
2026-059420.699297

Worked solution and explanation

What this problem really is

Under the revenue-tracker costume, this is a two-level aggregation: an average of monthly totals, scoped to every month so far, then cut to whole dollars. Anyone can sum a month. What separates candidates is putting the cumulative average on top of the grouped totals instead of the raw total_amount rows, and noticing that 'cents dropped' is not 'rounded'. Average the raw rows and every figure collapses to a per-sale number in the tens of dollars. Round instead of truncating and February reads 9818 where the books say 9817.


Build it in two levels

Step 1: Total each month of the year

Keep only 2026 rows with strftime('%Y', transaction_date) = '2026', bucket by strftime('%Y-%m', transaction_date), and take SUM(total_amount). The refund and the voided zero sale stay in: revenue is net. No products join is needed, because revenue lives entirely on transactions.

Step 2: Make the monthly totals the rows

Wrap that aggregate in a subquery so each month is one row with one monthly_revenue. This is the crux: the average must read nine monthly figures, not two hundred sales.

Step 3: Scope the average to the months so far

AVG(monthly_revenue) OVER (ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) averages January through the current month. Without the ordered frame, every row would show the same full-year figure.

Step 4: Drop the cents with an integer cast

CAST(... AS INTEGER) truncates toward zero, which is what dropping the cents means. ROUND would push 9817.5 up to 9818, so it is the wrong tool here.


The solution

Monthly totals, then a framed cumulative average cast to whole dollars
SELECT month, monthly_revenue,
  CAST(AVG(monthly_revenue) OVER (ORDER BY month ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS INTEGER) AS cumulative_avg
FROM (
  SELECT strftime('%Y-%m', transaction_date) AS month,
         SUM(total_amount) AS monthly_revenue
  FROM transactions
  WHERE strftime('%Y', transaction_date) = '2026'
  GROUP BY strftime('%Y-%m', transaction_date)
)
ORDER BY month
Rounded average
ROUND(AVG(...)) gives February 9818 and May 9298. Both are a dollar more than the year has actually averaged.
Truncated average
CAST(AVG(...) AS INTEGER) gives February 9817 and May 9297: the cents are dropped, never carried up.

Common follow-up questions