N062-M1 Tier 5 · Expert · medium hr · Helix Systems

Return each `department_id` and its `total_salary` — the combined current salary across active employees in that department

Part of Choosing Between Subqueries, CTEs, and Joins in SQL

The problem

Scenario: Helix Systems' HR analytics team needs the total current salary cost broken out by department, drawing only from active employees and their current salary records.

Task: Write a query to return each department_id and its total_salary — the combined current salary across active employees in that department.

Assumptions:

  • An active employee has is_active equal to TRUE.
  • A current salary record has end_date recorded as a missing value.
  • A department's total_salary is the combined amount across the current salary records of its active employees.
  • The result covers only departments with at least one qualifying record.

Output:

  • One row per qualifying department.
  • Columns in this order: department_id, total_salary.
Schema · hr 4 tables
departments
id integer
name text
location text
budget numeric
salaries
id integer
employee_id integer
amount numeric
effective_date date
end_date? date
employees
id integer
name text
email text
department_id integer
manager_id? integer
hire_date date
title text
is_active boolean
job_history
id integer
employee_id integer
title text
department_id integer
start_date date
end_date? date

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution Try it yourself first
Solution query
WITH
  active_employees AS (
    SELECT
      id AS employee_id,
      department_id
    FROM
      employees
    WHERE
      is_active = TRUE
  ),
  current_dept_salaries AS (
    SELECT
      ae.department_id,
      SUM(s.amount) AS total_salary
    FROM
      active_employees ae
      JOIN salaries s ON s.employee_id = ae.employee_id
    WHERE
      s.end_date IS NULL
    GROUP BY
      ae.department_id
  )
SELECT
  department_id,
  total_salary
FROM
  current_dept_salaries

The shape

Two CTEs cut the problem into two named layers: the first names "who counts" (active employees), the second sums their current salaries by department. Each layer reads as a labeled step; the final SELECT is just a hand-off.

Clause by clause

  • WITH active_employees AS (SELECT id AS employee_id, department_id FROM employees WHERE is_active = TRUE) names the pool of qualifying employees. The CTE keeps only is_active = TRUE rows and exposes the two columns the next stage needs.
  • current_dept_salaries AS (SELECT ae.department_id, SUM(s.amount) AS total_salary FROM active_employees ae JOIN salaries s ON s.employee_id = ae.employee_id WHERE s.end_date IS NULL GROUP BY ae.department_id) joins the active pool to salaries, restricts to current rows with s.end_date IS NULL, and totals by department. The JOIN (not LEFT JOIN) is deliberate — a department with no active employee or no current salary should not appear, which matches "covers only departments with at least one qualifying record."
  • SELECT department_id, total_salary FROM current_dept_salaries reads the second CTE through unchanged.

Why this and not a single query

SELECT e.department_id, SUM(s.amount) FROM employees e JOIN salaries s ON s.employee_id = e.id WHERE e.is_active = TRUE AND s.end_date IS NULL GROUP BY e.department_id produces the same numbers. The CTE form earns its space here because "active employees" is a concept worth naming — if the next analyst reads the query, the active_employees label tells them what the filter chain means before they trace it. For a one-shot aggregation, the flat form is leaner; pick on whether the named layer pays for itself.

The trap

Switching the second CTE's JOIN to LEFT JOIN would silently change the contract. A department with no current salary record would show up with SUM = NULL instead of being excluded, which contradicts "covers only departments with at least one qualifying record." The inner JOIN is doing the exclusion work; the outer query has no WHERE to fall back on.

You practiced staging the qualifying employees and their current salaries across two CTEs before producing the per-department total — each layer doing one transformation.

How you actually get good at SQL

Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.

That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

Real problems. Not toy examples.

615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

Write a query. Know if it's right in one second.

No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

Stop guessing whether you actually know it.

SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

Learn it once. Keep it for good.

Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.

Practice, feedback, mastery, review. That's the loop that turns reading into real skill.

Start free

No account, no credit card. Start solving in under a minute.