Batch Job Performance Tiers
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
- L4
- Asked in
- Travelport
Problem
The data platform team is grading batch job throughput for their quarterly review, where a job's grade comes from the total rows it processed across all of its runs. A job clearing 700 million rows is 'Outstanding', 350 million up to 700 million is 'Satisfactory', 200 million up to 350 million is 'Unsatisfactory', and anything below 200 million is 'Poor'. Show each job's name, its total rows processed, and its tier.
Every batch job gets a grade.
Tables
batch_jobs
Columns: job_id INTEGER, job_name TEXT, status TEXT, rows_done INTEGER, started TEXT, ended TEXT, priority INTEGER, retries INTEGER
| job_id | job_name | status | rows_done | started | ended | priority | retries |
|---|---|---|---|---|---|---|---|
| 1047 | user_export | running | 97 | 2026-02-02 01:00:00 | 2026-02-02 03:11:00 | 3 | 0 |
| 1094 | data_cleanup | failed | 194 | 2026-03-03 02:00:00 | 2026-03-03 04:22:00 | 6 | 0 |
| 1141 | email_blast | queued | 291 | 2026-04-04 03:00:00 | 2026-04-04 05:33:00 | 9 | 0 |
| 1188 | index_rebuild | canceled | 388 | 2026-05-05 04:00:00 | NULL | 2 | 0 |
| 1235 | cache_warm | Completed | 485 | 2026-06-06 05:00:00 | 2026-06-06 07:55:00 | 5 | 0 |
| 1282 | log_archive | FAILED | NULL | 2026-07-07 06:00:00 | 2026-07-07 08:06:00 | 8 | 0 |
| 1329 | metric_agg | completed | 679 | 2026-08-08 07:00:00 | 2026-08-08 09:17:00 | 1 | 0 |
| 1376 | backup_db | running | 776 | 2026-09-09 08:00:00 | NULL | 4 | 0 |
| 1423 | sync_elastic | failed | 873 | 2026-01-10 09:00:00 | 2026-01-10 11:39:00 | 7 | 5 |
| 1470 | daily_report | queued | 970 | 2026-02-11 10:00:00 | 2026-02-11 12:50:00 | 0 | 0 |
Expected output
| job_name | total_rows | performance_tier |
|---|---|---|
| backup_db | 74884 | Poor |
| cache_warm | 97000 | Poor |
| daily_report | 71780 | Poor |
| data_cleanup | 66736 | Poor |
| email_blast | 93120 | Poor |
Worked solution and explanation
What this problem really is
Beneath the quarterly-review framing this is a bucketing problem: collapse many job runs down to a per-name total, then map that single aggregate onto ordered tier labels. The trap is treating the CASE as decoration you can skip: the platform's per-name totals actually straddle the 200M/350M/700M cutoffs, so the label IS the answer, not the raw SUM. Two ways candidates blow it: they order the CASE branches low-to-high so the 200-million branch swallows every job, or they group by job_id and report per-run rows instead of per-name throughput. Either way the tiers come out wrong and a top performer gets stamped 'Poor'.
Break down the requirements
Step 1: Pick the grain
batch_jobs carries both job_id and job_name. The prompt grades 'each job's name', so collapse to job_name with GROUP BY job_name. Ask out loud whether the many job_id rows per name are reruns to sum (they are here) or whether you want a max.
Step 2: Aggregate rows
SUM(rows_done) AS total_rows. Sum, not avg, because the tier is about total throughput over the quarter, not per-run average. SUM also skips the null rows_done rows for free, so a crashed run that never logged a count just contributes nothing.
Step 3: Bucket with CASE WHEN
Order branches high-to-low so the first true one wins: >= 700000000 'Outstanding', then >= 350000000, then >= 200000000, else 'Poor'. Wrapping SUM(rows_done) in CASE is fine; the planner computes the aggregate once per group.
Step 4: Sort the output
ORDER BY total_rows DESC so the platform PM reads top throughput first, with job_name as a stable tiebreaker. Don't sort by the tier label; alphabetical 'Outstanding' before 'Poor' is meaningless to a reader.
The solution
SELECT
job_name,
SUM(rows_done) AS total_rows,
CASE
WHEN SUM(rows_done) >= 700000000 THEN 'Outstanding'
WHEN SUM(rows_done) >= 350000000 THEN 'Satisfactory'
WHEN SUM(rows_done) >= 200000000 THEN 'Unsatisfactory'
ELSE 'Poor'
END AS performance_tier
FROM batch_jobs
GROUP BY job_name
;COMMON FOLLOW-UP QUESTIONS
Common follow-up questions
- How would you rewrite this so the tier thresholds come from a config table? (Tests whether you reach for a small thresholds CTE or table plus `BETWEEN`, and whether you handle the quarter-over-quarter SCD.)
- Show the count and percentage of jobs in each tier. (Probes a second aggregation layer: wrap this query in a CTE and `GROUP BY performance_tier` with `COUNT(*) * 1.0 / SUM(COUNT(*)) OVER ()`.)
- How does the answer change if `rows_done` can be NULL for jobs that crashed mid-run? (Tests whether you remember `SUM` ignores NULL but `COALESCE(rows_done, 0)` is safer if you ever divide, plus whether crashed runs should be excluded entirely.)
- The PM wants a 5th tier 'Elite' at 1 billion rows and above. How do you add it without rewriting? (Reinforces the config-table answer and shows you anticipate threshold churn.)