Error Hall of Fame
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
- City and County of San Francisco
Problem
The on-call team's weekly error review lists TypeError and TYPEERROR as two separate problems, which splits the biggest offender in half. Rebuild the review from the err_tracks table: every error type with how many times it occurred, most frequent first.
The year's worst error categories.
Tables
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
| error_type | occurrences |
|---|---|
| typeerror | 39489 |
| nullpointerexception | 34252 |
| connectiontimeout | 19370 |
| permissiondenied | 18765 |
| parseerror | 18553 |
Worked solution and explanation
This is a rollup read dressed up as an error leaderboard. err_tracks does not hold one row per error event: each row is a tracked error with its own tally in count, and the same error is spelled more than one way in err_type. Anyone can write a grouped count. What separates candidates is reading the row before writing the query. If you count rows, every raw spelling ties at 20. If you group the raw string, TypeError shows up twice, so the real leader, 'typeerror' at 39,489, never appears as one line.
Two ways the obvious query lies
Step 1: Decide what one row means
Look at the sample before you write the aggregate. count holds 8, 15, 22: those are occurrences the tracker already tallied, so a type's total is SUM(count). COUNT(*) answers a different question (how many tracked errors exist), and on this seed it returns 20 for every spelling.
Step 2: Fold the spelling before you group
'TypeError' and 'TYPEERROR' are one error. Group on LOWER(err_type) and alias it error_type, so the bucket and the label are the same expression. Grouping the raw column keeps 10 rows where the review needs 8.
Step 3: Sort by the total, biggest first
ORDER BY occurrences DESC puts triage's first stop on top. The eight totals all differ on this data, so no tiebreaker is needed.
SELECT
LOWER(err_type) AS error_type,
SUM(count) AS occurrences
FROM err_tracks
GROUP BY LOWER(err_type)
ORDER BY occurrences DESC;- Row count on the raw spelling
GROUP BY err_typewithCOUNT(*): 10 rows, every one showing 20. 'TYPEERROR' and 'TypeError' sit on separate lines, and nothing in the list says which error fires most.- Tally on the folded spelling
GROUP BY LOWER(err_type)withSUM(count): 8 rows, 'typeerror' at 39,489 on top and 'nullpointerexception' at 34,252 second. The order finally means something.
Common follow-up questions
- How would you add each error type's share of all occurrences? (Tests a whole-result total alongside each group, without a second pass over `err_tracks`.)
- What if the review should cover only errors first seen in the last 30 days? (Tests filtering on `first_at` before the aggregate, and whether the partitioning then prunes the scan.)
- What if the tracker starts writing 'Type Error' with a space as well? (Tests whether the cleaning rule generalizes past case: stripping spaces, or a mapping table of known aliases.)