Service With Most Critical Alerts

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
City and County of San Francisco

Problem

Severity labels in alert_events were never normalized, so any label containing 'critical', in any casing, marks a critical alert, and the service with the most of them is the one the incident commander is triaging. Return every alert that service has fired, critical or not, exactly as it was logged, with each row also carrying that service's critical alert count.

One service keeps setting off the alarms.

Tables

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 26 rows
alert_idsvc_nameseveritystatusfired_atack_byresolvedcritical_alerts
216gatewayCriticalfiring2026-05-05 04:28:00NULL2026-05-05 06:44:0018
448gatewaycriticalfiring2026-04-13 12:24:00NULLNULL18
680gatewaymediumfiring2026-03-21 20:20:00NULL2026-03-21 22:40:0018
912gatewayCriticalfiring2026-02-01 04:16:00NULL2026-02-01 06:08:0018
1144gatewaycriticalfiring2026-01-09 12:12:00NULLNULL18

Worked solution and explanation

Two predicates, one costume

This is a two-predicate problem dressed up as incident triage. One predicate decides who wins: only alerts whose severity contains 'critical', in any casing, count toward the leader. A different predicate decides what you hand back: every alert that service fired, at any severity. Anyone can count critical alerts per service. What separates candidates is refusing to let that filter leak into the final rows. Leak it and the commander's view silently loses every 'medium' and 'high' alert from the service that is on fire.

There is a second, quieter test. Each row also carries the service's critical count, and that number has to come from the critical subset, not from a plain count of the service's alerts. The two numbers look alike in a preview until you check them against the rows you returned.

Step 1: Count critical alerts per service

Filter alert_events with severity ILIKE '%critical%' so 'Critical' and 'critical' land in one bucket, then GROUP BY svc_name with COUNT(*). An exact severity = 'critical', or a case-sensitive LIKE, misses the capitalized labels, so the count comes back short and can even point at the wrong service.

Step 2: Keep only the leader in top_service

Sort the counts highest first and LIMIT 1 inside the CTE. That ORDER BY is a selection tool, not presentation: it decides which single row survives the cut, and the final rows carry no promised order.

Step 3: Go back to the full table for the rows

The outer query reads alert_events again with only ae.svc_name IN (SELECT svc_name FROM top_service). No severity predicate here, and no rewriting of the labels: each alert comes back as it was logged. The scalar (SELECT critical_alerts FROM top_service) stamps the count onto every row, so the commander sees the whole history and the number that put this service on top.

Pick the winner, then return everything it fired
WITH top_service AS (
    SELECT svc_name, COUNT(*) AS critical_alerts
    FROM alert_events
    WHERE severity ILIKE '%critical%'
    GROUP BY svc_name
    ORDER BY critical_alerts DESC
    LIMIT 1
)
SELECT
    ae.alert_id,
    ae.svc_name,
    ae.severity,
    ae.status,
    ae.fired_at,
    ae.ack_by,
    ae.resolved,
    (SELECT critical_alerts FROM top_service) AS critical_alerts
FROM alert_events ae
WHERE ae.svc_name IN (SELECT svc_name FROM top_service)

The ILIKE lives only in top_service; the outer scan is unfiltered by severity.

Filter leaks into the output
Repeating severity ILIKE '%critical%' in the outer WHERE returns only the leader's critical alerts. The row count drops, the 'medium' firing alert vanishes, and the result still looks plausible.
Filter stays in the ranking
The ILIKE sits only inside top_service. The outer query filters on svc_name alone, so every alert the service fired comes back, each tagged with critical_alerts.
The leaky version (wrong)
SELECT ae.*
FROM alert_events ae
WHERE ae.severity ILIKE '%critical%'
  AND ae.svc_name IN (SELECT svc_name FROM top_service)

Returns a truncated history: only the rows that won the ranking, not the service's full record.

Common follow-up questions