Rollback Roulette
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
- Virgin Group
Problem
Count how many deployments in deploy_logs ended in a failed status, across every service and environment the log covers. Several CI tools have written to that log over the years, so a failure counts however the tool happened to spell its status.
Some ships sink before they leave the harbor.
Tables
deploy_logs
Columns: log_id INTEGER, svc_name TEXT, version TEXT, env_name TEXT, status TEXT, deploy_at TEXT, dur_secs INTEGER, author TEXT
| log_id | svc_name | version | env_name | status | deploy_at | dur_secs | author |
|---|---|---|---|---|---|---|---|
| 131 | payment-api | v1.0.2 | staging | failed | 2026-02-02 01:11:00 | 23 | bob |
| 162 | user-svc | v1.1.0 | dev | rolled_back | 2026-03-03 02:22:00 | 36 | charlie |
| 193 | search-api | 2.0.0 | canary | in_progress | 2026-04-04 03:33:00 | 49 | dana |
| 224 | gateway | v2.1.0 | Production | Success | 2026-05-05 04:44:00 | 62 | eve |
| 255 | notif-svc | 2.1.1 | STAGING | FAILED | 2026-06-06 05:55:00 | 75 | frank |
| 286 | analytics | v3.0.0-rc1 | production | success | 2026-07-07 06:06:00 | 88 | Alice |
| 317 | ml-serving | v1.0.3 | staging | failed | 2026-08-08 07:17:00 | NULL | BOB |
| 348 | auth-svc | v1.0.1 | dev | Rolled_Back | 2026-09-09 08:28:00 | 114 | alice |
| 379 | Payment-API | v1.0.2 | canary | in_progress | 2026-01-10 09:39:00 | 127 | bob |
| 410 | user-svc | v1.1.0 | Production | Success | 2026-02-11 10:50:00 | 140 | charlie |
Expected output
| failed_deployments |
|---|
| 66 |
Worked solution and explanation
What this is really asking
This is a data-cleaning question wearing an incident-review costume. Counting rows behind one predicate is trivial; the skill is noticing that status is not one vocabulary but several, written by different CI tools, and that 'failed' and 'FAILED' are the same outcome. Anyone writes COUNT(*) with a WHERE. What separates candidates is folding case on the column before comparing. Skip it and the count comes back short by exactly the failures one tool chose to shout.
Building it
Step 1: Fold status to one case
Wrap the column, not the value: LOWER(status) maps 'FAILED', 'Failed' and 'failed' onto one string, so a single comparison catches every tool's spelling, including one nobody has written yet.
Step 2: Match only the failed outcome
Compare the folded value to 'failed' and nothing else. 'rolled_back' and 'in_progress' are different outcomes; a rollback means the failure was handled, and an in-flight deploy has not ended at all.
Step 3: Count the rows that survive
COUNT(*) over the surviving rows, aliased to failed_deployments. Every row is one deploy and log_id is unique, so there is nothing to deduplicate.
SELECT COUNT(*) AS failed_deployments
FROM deploy_logs
WHERE LOWER(status) = 'failed'- Exact match
WHERE status = 'failed'returns a believable number and never errors. It also skips every row a tool logged as 'FAILED', so the total is quietly low.- Folded match
WHERE LOWER(status) = 'failed'treats every casing as the same outcome, so the count reflects what actually happened rather than which tool happened to log it.
Common follow-up questions
Common follow-up questions
- What share of all deploys failed? (Conditional aggregation over the whole table, still folding `status` before comparing.)
- Which service has the most failed deploys? (`svc_name` has its own casing and padding dirt, so the grouping key needs cleaning too.)
- How would you split failures by `env_name`? (`env_name` mixes 'production' and 'Production', so the same folding applies to the grouping column.)