The Full Picture
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
- L5
- Asked in
- DoubleVerify
Problem
The people analytics team is assembling a first-quarter department scorecard, putting each department's total payroll beside the combined Q1 metric value on record for that department. Departments with nothing logged for Q1 still belong on it, reading zero for the metric rather than dropping off.
Two tables know different things about the same people. Combine them.
Tables
employees
Columns: employee_id INTEGER, emp_name TEXT, department TEXT, salary REAL, manager_id INTEGER, hire_date TEXT, user_id INTEGER
| employee_id | emp_name | department | salary | manager_id | hire_date | user_id |
|---|---|---|---|---|---|---|
| 1 | Alice Chen | Engineering | 55731 | NULL | 2021-02-02 | 100 |
| 2 | Bob Martinez | Product | 61462 | NULL | 2022-03-03 | 197 |
| 3 | Carol Wu | Design | 67193 | NULL | 2023-04-04 | 294 |
| 4 | David Kim | Marketing | 72924 | NULL | 2024-05-05 | 391 |
| 5 | Eve Johnson | Sales | 78655 | NULL | 2025-06-06 | 488 |
| 6 | Frank Patel | HR | 84386 | NULL | 2026-07-07 | 585 |
| 7 | Grace Lee | Finance | 55117 | NULL | 2020-08-08 | 682 |
| 8 | Hank Brown | Operations | 60848 | NULL | 2021-09-09 | 779 |
| 9 | Iris Davis | Data | 66579 | NULL | 2022-10-10 | 876 |
| 10 | Jake Wilson | Legal | 72310 | NULL | 2023-11-11 | 973 |
employee_metrics
Columns: metric_id INTEGER, department TEXT, metric_value REAL, metric_name TEXT, fiscal_quarter TEXT, fiscal_year INTEGER
| metric_id | department | metric_name | metric_value | fiscal_quarter | fiscal_year |
|---|---|---|---|---|---|
| 141 | Product | attrition_rate | 7.3 | Q2 | 2025 |
| 182 | Design | avg_tenure_months | 14.6 | Q3 | 2026 |
| 223 | Marketing | satisfaction_score | 21.9 | Q4 | 2024 |
| 264 | Sales | training_hours | 29.2 | Q1 | 2025 |
| 305 | HR | promotions | 36.5 | Q2 | 2026 |
| 346 | Finance | open_positions | 43.8 | Q3 | 2024 |
| 387 | Legal | time_to_hire_days | 51.1 | Q4 | 2025 |
| 428 | Operations | diversity_ratio | 58.4 | Q1 | 2026 |
| 469 | Data | remote_percentage | 65.7 | Q2 | 2024 |
| 510 | Engineering | headcount | 73 | Q3 | 2025 |
Expected output
| department | total_payroll | q1_metric_total |
|---|---|---|
| Data | 2079480 | 0 |
| Design | 2021760 | 528.8 |
| Engineering | 1932520 | 412 |
| Finance | 2060240 | 252.79999999999998 |
| HR | 2085620 | 0 |
Worked solution and explanation
What this problem is really about
This is a two-grain problem dressed as a scorecard. Payroll lives on employees, one row per person. The Q1 readings live on employee_metrics, many rows per department. Join the two on department and each of the five departments that logged Q1 comes back as 200 rows: 20 employees times 10 Q1 readings. SUM(e.salary) then counts every salary 10 times, and SUM(m.metric_value) counts every reading 20 times, and the numbers still look plausible. Anyone can write the join. The skill is noticing that each measure has to be totalled at the side that holds it before the two meet.
Building the query
Step 1: Total payroll before the join
SELECT department, SUM(salary) AS total_payroll FROM employees GROUP BY department sits in the FROM as payroll, giving ten rows, one per department. A CTE would read the same; the point is that salary is summed before anything can repeat it.
Step 2: Join the Q1 rows inside the ON
LEFT JOIN employee_metrics m ON m.department = payroll.department AND m.fiscal_quarter = 'Q1'. Each department meets only its own Q1 readings, and the five departments that logged nothing keep one row with an empty match.
Step 3: Sum the readings and zero-fill
Group by payroll.department, payroll.total_payroll so the payroll is carried as a key and never summed again. COALESCE(SUM(m.metric_value), 0) turns an empty match into 0 instead of NULL.
The solution
SELECT payroll.department,
payroll.total_payroll,
COALESCE(SUM(m.metric_value), 0) AS q1_metric_total
FROM (
SELECT department, SUM(salary) AS total_payroll
FROM employees
GROUP BY department
) payroll
LEFT JOIN employee_metrics m
ON m.department = payroll.department
AND m.fiscal_quarter = 'Q1'
GROUP BY payroll.department, payroll.total_payroll- Join, then total
- Every employee row meets every Q1 reading of the department.
SUM(e.salary)sees each salary 10 times andSUM(m.metric_value)sees each reading 20 times in the five Q1 departments. - Total, then join
payrollhas one row per department, so the join attaches at most 10 readings to a singletotal_payroll. Each measure is summed once, on its own side.
SELECT e.department,
SUM(e.salary) AS total_payroll,
COALESCE(SUM(m.metric_value), 0) AS q1_metric_total
FROM employees e
LEFT JOIN employee_metrics m
ON m.department = e.department
AND m.fiscal_quarter = 'Q1'
GROUP BY e.departmentKeeps all ten departments, but inflates both totals wherever Q1 was logged.
Common follow-up questions
- What changes if you need Q1 of a single `fiscal_year` only? (Tests whether they add the year predicate to the `ON` clause, not a `WHERE`.)
- How would you add each department's headcount without the Q1 rows inflating it? (Tests counting inside `payroll`, on the `employees` side, rather than after the join.)
- If metrics were logged per employee instead of per department, what would the join key and grain become? (Tests re-deriving which side is the one and which the many.)