Open Wounds
A hard SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- hard
- Seniority
- L4
- Asked in
Problem
During an incident review, the on-call team needs the oldest unresolved alert for each service. Walk the services in alphabetical order, showing each one's name, the alert ID, the severity, the status, and the time the alert was fired.
Every service has one alarm that was never silenced. Find the one that has been screaming the longest.
Tables
alert_events
Columns: alert_id INTEGER, svc_name TEXT, severity TEXT, status TEXT, fired_at TEXT, ack_by TEXT, resolved TEXT
| alert_id | svc_name | severity | status | fired_at | ack_by | resolved |
|---|---|---|---|---|---|---|
| 129 | payment-api | high | acknowledged | 2026-02-02 01:07:00 | bob | 2026-02-02 03:11:00 |
| 158 | user-svc | medium | resolved | 2026-03-03 02:14:00 | charlie | 2026-03-03 04:22:00 |
| 187 | search-api | low | silenced | 2026-04-04 03:21:00 | dana | NULL |
| 216 | gateway | Critical | firing | 2026-05-05 04:28:00 | NULL | 2026-05-05 06:44:00 |
| 245 | notif-svc | HIGH | acknowledged | 2026-06-06 05:35:00 | alice | 2026-06-06 07:55:00 |
| 274 | db-primary | critical | resolved | 2026-07-07 06:42:00 | bob | NULL |
| 303 | cache-01 | high | silenced | 2026-08-08 07:49:00 | charlie | 2026-08-08 09:17:00 |
| 332 | auth-svc | medium | firing | 2026-09-09 08:56:00 | NULL | 2026-09-09 10:28:00 |
| 361 | payment-api | low | acknowledged | 2026-01-10 09:03:00 | eve | NULL |
| 390 | user-svc | Critical | resolved | 2026-02-11 10:10:00 | alice | 2026-02-11 12:50:00 |
Expected output
| svc_name | alert_id | severity | status | fired_at |
|---|---|---|---|---|
| auth-svc | 10003096 | critical | firing | 2022-12-13 00:12:00 |
| cache-01 | 10003087 | low | silenced | 2023-03-04 15:09:00 |
| db-primary | 10003006 | critical | resolved | 2022-06-07 06:42:00 |
| gateway | 10003036 | critical | firing | 2022-12-09 12:12:00 |
| notif-svc | 10003021 | low | acknowledged | 2022-09-22 21:27:00 |
Worked solution and explanation
What this really is
This is a per-group earliest-row pick wearing an incident-management costume, and the row has to come back whole: id, severity and status of the winning alert, not just its time. Everyone lands the window function. What separates candidates is the single word 'unresolved'. Two columns describe closure and they disagree: an operator can flip status to 'resolved' while the resolved timestamp was never written, and leave status at 'firing' on an alert closed hours ago. Trust the label and you drop alerts that are still bleeding and crown closed ones as the oldest open. The timestamp is the authority: open means resolved IS NULL.
Reading the requirements
Step 1: Filter to genuinely open alerts
WHERE resolved IS NULL keeps only alerts nobody ever closed. It never touches status, because the label and the timestamp contradict each other in this data.
Step 2: Number oldest-first within each service
ROW_NUMBER() OVER (PARTITION BY svc_name ORDER BY fired_at ASC) numbers each service's open alerts from oldest to newest, so the earliest fired alert per service gets 1. ROW_NUMBER (not RANK) hands out exactly one 1 per service even if two alerts fired at the same instant.
Step 3: Keep the winner per service
Wrap it in ranked_open_alerts and keep WHERE rn = 1. The whole row (id, severity, status) rides along with the winning fired_at, which a per-service MIN(fired_at) cannot give you without a second trip back to the table.
Step 4: Order the output by service
ORDER BY svc_name gives the reviewer the alphabetical read of the board the statement asks for.
The solution
SELECT svc_name, alert_id, severity, status, fired_at
FROM (
SELECT
svc_name, alert_id, severity, status, fired_at,
ROW_NUMBER() OVER (PARTITION BY svc_name ORDER BY fired_at ASC) AS rn
FROM alert_events
WHERE resolved IS NULL
) AS ranked_open_alerts
WHERE rn = 1
ORDER BY svc_name- status <> 'resolved'
- Reads the label. db-primary's alert 274 says 'resolved' with a null
resolvedtimestamp, so it is still open, and this filter throws it away. On the seed db-primary's true oldest open alert, 10003006, is labelled 'resolved' too, so the service gets the wrong winner, while gateway's alert 216 reads 'firing' despite being closed and gets let in. - resolved IS NULL
- Reads the authority. It keeps exactly the alerts no one ever closed, whatever the status text claims, and admits none that were closed. Status is still shown in the output, just never used to decide.
Common follow-up questions
- Suppose a later migration lets an alert be reopened, clearing `resolved` back to NULL after it was set. Does your query still return the right oldest-open alert, and what would break if you had filtered on `status` instead? (Tests whether the filter is held to closure semantics rather than the label under a schema change.)
- If two open alerts for one service share the exact same `fired_at`, how many rows does your query return for that service, and how would you make the winner deterministic? (Tests `ROW_NUMBER` vs `RANK` vs `DENSE_RANK` tie behaviour and adding a tiebreaker such as `alert_id`.)
- Which index would you build so this stays fast at 30M rows, and why does column order in that index matter for both the filter and the per-service ordering? (Tests indexing intuition for a filtered, partitioned, ordered scan at scale.)