Top Error-Service Pair

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

Problem

During an incident postmortem, you need the error-and-service pairing behind the most resolved incidents. Pair each error type with its service name from the error tracker using a hyphen, then count the separate resolved alerts each pairing accounts for, where an alert counts as resolved only if its resolved field is filled in. Return the top pairings including ties, listed alphabetically by the pairing.

Which error-service pair triggered the most resolved incidents

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

alert_events

Columns: alert_id INTEGER, svc_name TEXT, severity TEXT, status TEXT, fired_at TEXT, ack_by TEXT, resolved TEXT

alert_events: 10 of 200 rows
alert_idsvc_nameseveritystatusfired_atack_byresolved
129payment-apihighacknowledged2026-02-02 01:07:00bob2026-02-02 03:11:00
158user-svcmediumresolved2026-03-03 02:14:00charlie2026-03-03 04:22:00
187search-apilowsilenced2026-04-04 03:21:00danaNULL
216gatewayCriticalfiring2026-05-05 04:28:00NULL2026-05-05 06:44:00
245notif-svcHIGHacknowledged2026-06-06 05:35:00alice2026-06-06 07:55:00
274db-primarycriticalresolved2026-07-07 06:42:00bobNULL
303cache-01highsilenced2026-08-08 07:49:00charlie2026-08-08 09:17:00
332auth-svcmediumfiring2026-09-09 08:56:00NULL2026-09-09 10:28:00
361payment-apilowacknowledged2026-01-10 09:03:00eveNULL
390user-svcCriticalresolved2026-02-11 10:10:00alice2026-02-11 12:50:00

Expected output

First 5 of 15 rows
reporter_identityresolved_incidents
ConnectionTimeout-gateway18
ConnectionTimeout-user-svc18
FileNotFound-gateway18
FileNotFound-user-svc18
NullPointerException-gateway18

Worked solution and explanation

What this problem really is

This is a ranking-with-ties over a fan-out join, wearing a postmortem costume. The real question: which err_type-svc_name pairing owns the most resolved alerts, when the only shared key between the error tracker and the alert log is svc_name? That join multiplies every alert by the number of error rows on its service, so COUNT(*) counts the fan-out, not incidents. And resolved is a nullable timestamp, so skip the IS NOT NULL test and you fold every still-firing alert into the total. Get either wrong and the leaderboard is fiction.


Break down the requirements

Step 1: Confirm the join key

Join err_tracks to alert_events on svc_name, the only key the two tables share. There is no err_id on the alert side, so the grain of the join is one error row against every alert for that service.

Step 2: Filter to resolved alerts with the NULL-aware test

resolved holds the resolution timestamp and is NULL for alerts that never closed. a.resolved IS NOT NULL scopes the count to resolved incidents. This filter is load-bearing: about one alert in five is still open, and counting those turns the total into alert volume rather than resolved incidents.

Step 3: Build the composite identifier

Most engines do not allow COUNT(DISTINCT col1, col2), so build the key with e.err_type || '-' || e.svc_name AS reporter_identity and group on it. Pick a delimiter that cannot appear in either column.

Step 4: Count distinct resolved incidents per pairing

COUNT(DISTINCT a.alert_id) collapses the fan-out: the svc_name join repeats each alert once per matching error row, so a plain COUNT(*) multiplies each incident by the number of error types on that service. The DISTINCT counts each resolved incident exactly once.

Step 5: Rank with ties, order alphabetically

RANK() OVER (ORDER BY COUNT(DISTINCT a.alert_id) DESC) then filter rnk = 1. Use RANK (not ROW_NUMBER) so tied leaders all surface. A final ORDER BY reporter_identity gives the alphabetical sequence, and since each pairing appears once the order is total.


The solution

Resolved incidents per error-service pairing
SELECT reporter_identity, resolved_incidents
FROM (
  SELECT e.err_type || '-' || e.svc_name AS reporter_identity,
         COUNT(DISTINCT a.alert_id) AS resolved_incidents,
         RANK() OVER (ORDER BY COUNT(DISTINCT a.alert_id) DESC) AS rnk
  FROM err_tracks e
  JOIN alert_events a ON e.svc_name = a.svc_name
  WHERE a.resolved IS NOT NULL
  GROUP BY e.err_type || '-' || e.svc_name
) ranked
WHERE rnk = 1
ORDER BY reporter_identity

Common follow-up questions

Common follow-up questions