CTEs & Window Functions SQL practice problemHardVerified answer

Monthly Salary Expense — MoM Growth & Rolling Average

Return a chained CTE salary growth summary including baseline, current, and growth metrics.

  • CTEs
  • Window functions
  • Joins
  • Subqueries
  • Aggregation

Interview brief

Understand the request

Workforce strategy director The salary growth analysis chains CTEs to compute department baselines, growth rates, and cumulative trends in one report.

From salaries_history, build monthly salary expense per department (month = first 7 chars of effective_date, YYYY-MM). Then compute month-over-month growth %, a 3-month rolling average, and a has_growth flag. Return department_name, expense_month, monthly_expense, prev_month_expense, mom_growth_pct, rolling_3month_avg, has_growth — ordered by department_name, expense_month.

Return

  • Return the columns shown in the expected output for this complex chain.

Constraints

  • Use multiple CTEs where each builds on the previous to compute the final growth metrics.

Data you will use

Review the relevant tables before deciding how to join, filter, or aggregate them.

salaries_history

  • employee_idINTEGER
  • salaryINTEGER
  • effective_dateDATE

employees

  • employee_idINTEGER
  • department_idINTEGER

departments

  • department_idINTEGER
  • department_nameVARCHAR(50)

Hints, when you need them

Open one clue at a time so you still do the reasoning.

Hint 1

substr(effective_date, 1, 7) gives 'YYYY-MM' on every engine — no strftime/TO_CHAR needed.

Hint 2

NULLIF(prev, 0) guards the growth % against divide-by-zero.

Hint 3

The rolling average uses an explicit ROWS BETWEEN 2 PRECEDING AND CURRENT ROW frame (last 3 months).

Verified SQL answer

Attempt the problem first, then compare structure and reasoning—not just syntax.

Reveal solution and explanation
WITH monthly AS (SELECT e.department_id, d.department_name, substr(sh.effective_date, 1, 7) AS expense_month, SUM(sh.salary) AS monthly_expense FROM salaries_history sh INNER JOIN employees e ON sh.employee_id = e.employee_id INNER JOIN departments d ON e.department_id = d.department_id GROUP BY e.department_id, d.department_name, substr(sh.effective_date, 1, 7)) SELECT department_name, expense_month, monthly_expense, LAG(monthly_expense) OVER (PARTITION BY department_id ORDER BY expense_month) AS prev_month_expense, ROUND((monthly_expense - LAG(monthly_expense) OVER (PARTITION BY department_id ORDER BY expense_month)) * 100.0 / NULLIF(LAG(monthly_expense) OVER (PARTITION BY department_id ORDER BY expense_month), 0), 2) AS mom_growth_pct, ROUND(AVG(monthly_expense) OVER (PARTITION BY department_id ORDER BY expense_month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), 2) AS rolling_3month_avg, CASE WHEN monthly_expense > LAG(monthly_expense) OVER (PARTITION BY department_id ORDER BY expense_month) THEN 'YES' ELSE 'NO' END AS has_growth FROM monthly ORDER BY department_name, expense_month;

Why this works

Month-over-month analysis is a BI staple. Bucketing the month with substr keeps it portable; LAG provides the prior period; an explicit ROWS frame gives the rolling average. NULLIF prevents the classic divide-by-zero on the first month.

Success check

The result should read like a multi-stage financial growth analysis, not a simple roster.

Expected result

Use this output to verify values, aliases, ordering, and row count.

department_nameexpense_monthmonthly_expenseprev_month_expensemom_growth_pctrolling_3month_avghas_growth
Engineering2020-02110000NULLNULL110000NO
Engineering2020-05105000110000-4.55107500NO
Engineering2021-0185000105000-19.05100000NO
Engineering2021-021200008500041.18103333.33YES
Engineering2021-05115000120000-4.17106666.67NO
Engineering2022-0195000115000-17.39110000NO
Engineering2022-021300009500036.84113333.33YES
Engineering2022-05125000130000-3.85116666.67NO
Sales2020-0895000NULLNULL95000NO
Sales2021-066800095000-28.4281500NO

Previewing 10 of 13 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: