Errors With Service Health

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
ESPN

Problem

List every error in err_tracks beside each health reading svc_health holds for its service, showing the error's ID, type, message, service name, severity, count and first occurrence with the reading's status, latency and uptime. The on-call engineer triaging the log wants the full check history behind each error, not just the latest reading, and only for errors whose service has at least one reading on record. Types and severities go out exactly as the log recorded them, casing and all.

Error data, enriched with health context.

Tables

err_tracks

Columns: err_id INTEGER, err_type TEXT, message TEXT, svc_name TEXT, severity TEXT, count INTEGER, first_at TEXT

err_tracks: 10 of 200 rows
err_iderr_typemessagesvc_nameseveritycountfirst_at
137TypeErrorCannot read property of undefinedpayment-apierror82026-02-02 01:11:00
174ConnectionTimeoutConnection timed out after 30suser-svcwarning152026-03-03 02:22:00
211OutOfMemoryHeap space exhaustedsearch-apiinfo222026-04-04 03:33:00
248FileNotFoundFile /tmp/data.csv not foundgatewayFatal292026-05-05 04:44:00
285PermissionDeniedAccess denied for user rootworkerERROR362026-06-06 05:55:00
322RateLimitExceededToo many requests from 10.0.0.1auth-svcfatal432026-07-07 06:06:00
359ParseErrorMalformed JSON in request bodypayment-apierror502026-08-08 07:17:00
396nullpointerexceptionStack overflow in recursive calluser-svcwarning572026-09-09 08:28:00
433TYPEERRORDivision by zerosearch-apiinfo642026-01-10 09:39:00
470NullPointerExceptionUnexpected null value at line 42gatewayFatalNULL2026-02-11 10:50:00

svc_health

Columns: check_id INTEGER, svc_name TEXT, status TEXT, latency REAL, uptime REAL, checked TEXT, region TEXT

svc_health: 10 of 200 rows
check_idsvc_namestatuslatencyuptimecheckedregion
137payment-apidegraded3.697.032026-02-02 01:05:00us-west-2
174user-svcunhealthy6.794.062026-03-03 02:10:00eu-west-1
211search-apitimeout9.891.092026-04-04 03:15:00ap-south-1
248gatewayHealthy12.998.122026-05-05 04:20:00eu-central-1
285notif-svcDEGRADED1695.152026-06-06 05:25:00us-east-1
322ml-servinghealthy19.192.182026-07-07 06:30:00us-west-2
359cache-svcdegradedNULL99.212026-08-08 07:35:00eu-west-1
396queue-workerunhealthy25.396.242026-09-09 08:40:00ap-south-1
433cron-svctimeout28.493.272026-01-10 09:45:00eu-central-1
470auth-svcHealthy31.590.32026-02-11 10:50:00us-east-1

Expected output

First 5 of 3024 rows
err_iderr_typemessagesvc_nameseveritycountfirst_atstatuslatencyuptime
137TypeErrorCannot read property of undefinedpayment-apierror82026-02-02 01:11:00DEGRADED34.6NULL
174ConnectionTimeoutConnection timed out after 30suser-svcwarning152026-03-03 02:22:00DEGRADEDNULL97.55
211OutOfMemoryHeap space exhaustedsearch-apiinfo222026-04-04 03:33:00DEGRADED71.891.69
248FileNotFoundFile /tmp/data.csv not foundgatewayFatal292026-05-05 04:44:00DEGRADEDNULL91.65
322RateLimitExceededToo many requests from 10.0.0.1auth-svcfatal432026-07-07 06:06:00DEGRADEDNULL91.45

Worked solution and explanation

What this really is

This is record enrichment dressed up as on-call triage. Every error row knows which service failed, but not how that service was doing: status, latency and uptime live only in svc_health, so no query over err_tracks alone can return them. Anyone finds the shared svc_name. What trips people is that the same column name sits on both sides, so a bare svc_name in the select list raises an ambiguous-column error and the query never runs.

The second thing to notice: svc_health is a history, many checks per service, not one row per service. The statement asks for the full check history, so each error legitimately repeats once per reading. That fan-out is the answer here, not a bug to dedupe away.


Build it

Step 1: Find the column the two tables share

svc_name is the only column on both err_tracks and svc_health. It is not a key on either side: many errors and many checks point at the same service, which is exactly why rows multiply.

Step 2: Pair errors to readings and alias both sides

Alias the tables as e and s and match on e.svc_name = s.svc_name. An inner pairing also quietly honors the second half of the ask: an error whose service has no reading has nothing to pair with, so it drops out.

Step 3: Qualify every column you select

Take the seven error fields from e and the three health fields from s, passed through untouched. The statement wants types and severities as logged, so 'TYPEERROR' and 'TypeError' stay two different strings: normalizing them changes the graded values.

Every error beside every reading for its service
SELECT e.err_id, e.err_type, e.message, e.svc_name,
       e.severity, e.count, e.first_at,
       s.status, s.latency, s.uptime
FROM err_tracks e
JOIN svc_health s ON e.svc_name = s.svc_name

Qualifying e.svc_name picks one copy of the shared column and the ambiguity disappears.

Unqualified
SELECT err_id, svc_name, status FROM err_tracks JOIN svc_health ON ... fails: the engine cannot tell which table's svc_name you mean.
Qualified
SELECT e.err_id, e.svc_name, s.status ... names the side for every column, so the query reads unambiguously to the engine and to the next engineer.

Common follow-up questions