Cloud Cost by Team
An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- easy
- Seniority
- L3
- Asked in
- City and County of San Francisco
Problem
Credits land in cost_allocs as negative amounts, and they count against a team's spend. For the budget review, an analyst needs each team's total cost allocation, from highest to lowest.
Spend by team. Who's burning most?
Tables
cost_allocs
Columns: alloc_id INTEGER, team_name TEXT, svc_name TEXT, region TEXT, amount REAL, period TEXT, acct_id TEXT, category TEXT
| alloc_id | team_name | svc_name | region | amount | period | acct_id | category |
|---|---|---|---|---|---|---|---|
| 129 | data-eng | S3 | us-west-2 | 83.21 | 2026-02 | acct-101 | storage |
| 158 | ml-team | RDS | eu-west-1 | 156.42 | 2026-03 | acct-102 | network |
| 187 | frontend | Lambda | ap-south-1 | 229.63 | 2026-04 | acct-103 | database |
| 216 | backend | BigQuery | us-central1 | -120.5 | 2026-05 | acct-104 | ml |
| 245 | devops | CloudFront | europe-west1 | 376.05 | 2026-06 | acct-105 | other |
| 274 | security | EKS | ap-southeast-1 | 449.26 | 2026-07 | acct-106 | compute |
| 303 | mobile | SQS | eu-central-1 | 522.47 | 2026-08 | acct-107 | storage |
| 332 | Platform | EC2 | us-east-1 | 595.68 | 2026-09 | NULL | network |
| 361 | DATA-ENG | S3 | us-west-2 | 668.89 | 2026-01 | acct-109 | database |
| 390 | platform | RDS | eu-west-1 | 742.1 | 2026-02 | acct-110 | ml |
Expected output
| team_name | total_cost |
|---|---|
| platform | 180855.7 |
| DATA-ENG | 179212.3 |
| Platform | 177568.9 |
| data-eng | 175178.1 |
| mobile | 166250.72 |
Worked solution and explanation
This is one collapse of cost_allocs into a net total per team_name, dressed up as a budget review. Anyone writes SUM(amount). The grain is what separates candidates: the labels come in casing variants ('platform' and 'Platform', 'data-eng' and 'DATA-ENG'), and the expected output keeps each spelling as its own row. Reach for LOWER(team_name) to tidy them and you get 14 rows instead of 16, two totals inflated by the merge, a reshuffled top of the list, and a failed grade.
Getting to the query
Step 1: Settle the grain before writing anything
One output row per team_name value as stored. The preview settles it: 'platform' and 'Platform' each carry their own total, so the grouping key is the raw column with nothing wrapped around it.
Step 2: Sum every row, credits included
SUM(amount) nets the one negative row (a -120.5 credit on 'backend') against that team's spend. A WHERE amount > 0 guard looks careful and silently overstates backend.
Step 3: Sort on the alias, not the expression
ORDER BY total_cost DESC sorts on the aggregate you already named, so the sum is written once. The order is graded, so the sort is part of the answer, not presentation.
SELECT team_name, SUM(amount) AS total_cost
FROM cost_allocs
GROUP BY team_name
ORDER BY total_cost DESC- Tidied labels
GROUP BY LOWER(team_name)folds 'Platform' into 'platform' and 'DATA-ENG' into 'data-eng': 14 rows, two totals near 358k and 354k that exist nowhere in the expected output.- Labels as stored
GROUP BY team_namereturns 16 rows, every spelling with its own total, exactly the shape the preview shows.
Common follow-up questions
- What if you needed only the top 5 teams? (Tests `LIMIT 5` and why it needs the `ORDER BY` to mean anything.)
- How would you show each team's percentage of total spend? (Tests a window over the aggregate: `100.0 * SUM(amount) / SUM(SUM(amount)) OVER ()`.)
- What if 'platform' and 'Platform' should be one team? (Tests grouping on `LOWER(team_name)`, and whether the label belongs fixed upstream instead.)