Unmatched Deploy Services
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
- L3
Problem
We expect every service in deploy_logs to have reported at least once to the error tracker in err_tracks, and any that never has is probably shipping without error reporting wired up. The deploy log spells some service names with stray spaces or capital letters, so clean each to one lowercase name and list the silent services alphabetically.
Two registries. They do not agree.
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 |
err_tracks
Columns: err_id INTEGER, err_type TEXT, message TEXT, svc_name TEXT, severity TEXT, count INTEGER, first_at TEXT
| err_id | err_type | message | svc_name | severity | count | first_at |
|---|---|---|---|---|---|---|
| 137 | TypeError | Cannot read property of undefined | payment-api | error | 8 | 2026-02-02 01:11:00 |
| 174 | ConnectionTimeout | Connection timed out after 30s | user-svc | warning | 15 | 2026-03-03 02:22:00 |
| 211 | OutOfMemory | Heap space exhausted | search-api | info | 22 | 2026-04-04 03:33:00 |
| 248 | FileNotFound | File /tmp/data.csv not found | gateway | Fatal | 29 | 2026-05-05 04:44:00 |
| 285 | PermissionDenied | Access denied for user root | worker | ERROR | 36 | 2026-06-06 05:55:00 |
| 322 | RateLimitExceeded | Too many requests from 10.0.0.1 | auth-svc | fatal | 43 | 2026-07-07 06:06:00 |
| 359 | ParseError | Malformed JSON in request body | payment-api | error | 50 | 2026-08-08 07:17:00 |
| 396 | nullpointerexception | Stack overflow in recursive call | user-svc | warning | 57 | 2026-09-09 08:28:00 |
| 433 | TYPEERROR | Division by zero | search-api | info | 64 | 2026-01-10 09:39:00 |
| 470 | NullPointerException | Unexpected null value at line 42 | gateway | Fatal | NULL | 2026-02-11 10:50:00 |
Expected output
| service_name |
|---|
| analytics |
| ml-serving |
| notif-svc |
Worked solution and explanation
What this really is
This is a set difference between two registries that never agreed on spelling. Anyone can write "in A but not in B". What separates candidates is checking the left side first: deploy_logs stores 'payment-api' as 'Payment-API', ' payment-api' and 'payment-api ' too. Subtract the raw names and none of those variants match the tracker, so the noisiest service in the fleet shows up on the silent list three times.
Build it in three moves
Step 1: Clean the name, then collapse it
Select LOWER(TRIM(svc_name)) under SELECT DISTINCT and alias it service_name. The cleaning has to happen before the collapse: DISTINCT on the raw column keeps four rows for one service.
Step 2: Subtract what the tracker knows
Filter with WHERE LOWER(TRIM(svc_name)) NOT IN (SELECT svc_name FROM err_tracks). Notice the cleaning is written twice. WHERE runs before the select list exists, so the alias service_name is not available there, and testing the raw svc_name would let 'Payment-API' slip through as silent. The tracker side is already lowercase and unpadded and holds no NULL names, so NOT IN is safe as written.
Step 3: Sort by the cleaned name
Order by service_name, the column you return. Each name appears once, so the order is fully decided.
SELECT DISTINCT LOWER(TRIM(svc_name)) AS service_name
FROM deploy_logs
WHERE LOWER(TRIM(svc_name)) NOT IN (
SELECT svc_name FROM err_tracks
)
ORDER BY service_name- Raw names
- Six rows: 'analytics', 'ml-serving', 'notif-svc', plus 'Payment-API', ' payment-api' and 'payment-api '. Half the list is one healthy service under three spellings.
- Cleaned names
- Three rows: 'analytics', 'ml-serving', 'notif-svc'. Every entry is a real service that has never reported an error.
Common follow-up questions
- What if `err_tracks.svc_name` could be `NULL`? (Tests whether you switch to `NOT EXISTS` or filter the subquery.)
- How would you find services that report errors but have never been deployed? (Reverses the direction of the difference; the cleaning moves with it.)
- How would you limit this to services deployed to production in 2026? (The environment column is dirty too: 'Production' and 'production'.)