Calculate Period-over-Period Revenue Growth
Use a CTE to calculate previous_revenue once with LAG, then calculate absolute and percentage change.
- CTEs
- Window functions
- Subqueries
- Numeric functions
- NULL handling
Exercise brief
Understand the request
FP&A analyst A growth report needs change against the previous available observation without assuming periods are contiguous.
A growth report needs change against the previous available observation without assuming periods are contiguous. Use a CTE to calculate previous_revenue once with LAG, then calculate absolute and percentage change.
Return
- Return team_name, period_no, revenue, previous_revenue, revenue_change, and growth_pct.
- Round growth_pct to two decimals and order by team_name, period_no.
Constraints
- Calculate LAG in a CTE before deriving growth_pct.
- Use NULLIF(previous_revenue, 0) as the percentage denominator.
- Preserve NULL for each team’s first observation.
Data you will use
Review the relevant tables before deciding how to join, filter, or aggregate them.
team_metrics
snapshot_idINTEGERteam_nameVARCHAR(40)period_noINTEGERrevenueINTEGERtickets_closedINTEGERquality_scoreINTEGER
Hints, when you need them
Open one clue at a time so you still do the reasoning.
Hint 1
Compute the previous value once, then give it a name.
Hint 2
Arithmetic with the first row’s NULL previous value correctly stays NULL.
Hint 3
NULLIF protects the denominator if a future snapshot records zero revenue.
Verified SQL answer
Attempt the problem first, then compare structure and reasoning—not just syntax.
Reveal solution and explanation
WITH period_context AS (SELECT team_name, period_no, snapshot_id, revenue, LAG(revenue) OVER (PARTITION BY team_name ORDER BY period_no, snapshot_id) AS previous_revenue FROM team_metrics) SELECT team_name, period_no, revenue, previous_revenue, revenue - previous_revenue AS revenue_change, ROUND((revenue - previous_revenue) * 100.0 / NULLIF(previous_revenue, 0), 2) AS growth_pct FROM period_context ORDER BY team_name, period_no;Why this works
The CTE separates sequence context from business arithmetic, avoiding repeated windows and protecting denominator semantics.
Success check
Growth compares with the prior available row and cannot divide by zero.
Expected result
Use this output to verify values, aliases, ordering, and row count.
| team_name | period_no | revenue | previous_revenue | revenue_change | growth_pct |
|---|---|---|---|---|---|
| alpha | 1 | 100 | NULL | NULL | NULL |
| alpha | 2 | 120 | 100 | 20 | 20 |
| alpha | 3 | 120 | 120 | 0 | 0 |
| alpha | 5 | 90 | 120 | -30 | -25 |
| alpha | 6 | 150 | 90 | 60 | 66.67 |
| beta | 1 | 80 | NULL | NULL | NULL |
| beta | 2 | 110 | 80 | 30 | 37.5 |
| beta | 4 | 95 | 110 | -15 | -13.64 |
| beta | 5 | 95 | 95 | 0 | 0 |
| beta | 6 | 130 | 95 | 35 | 36.84 |
Previewing 10 of 14 expected rows. Run the query in the editor to inspect the full result.
Learn the concepts behind this answer
Strengthen your understanding with these targeted learning topics:
Continue practicing
Build the next SQL skill
Ranking & NTH Value
Solve deterministic ranking, top-N, distribution, positional-frame, and rolling-window problems.
SQL Subqueries
Practice scalar, derived-table, correlated, EXISTS, NULL-safe anti-subquery, quantified, and row-subquery patterns.
Self Joins & Hierarchical Queries
Query organization charts, trees, and parent-child relationships.
Open the interactive workspace and practice across SQL topics.