Regions by Alert Volume

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

Problem

We're building an incident heatmap by region, and on-call wants the noisiest regions surfaced first. Count the alerts per region (based on the service's region), from the most incidents down to the fewest, and when two regions carry the same count list them alphabetically.

Some regions are quiet. Others never stop screaming.

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

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 5 rows
regionalert_count
us-east-1688
eu-west-1644
ap-south-1468
eu-central-1468
us-west-2468

Worked solution and explanation

This is a dimension lookup dressed up as a heatmap. alert_events knows which service fired, never where it runs; region exists only on svc_health, so the real work is carrying that attribute over before anything gets tallied. Anyone writes the GROUP BY. What separates candidates is checking the grain of the table they borrowed from: svc_health is a log of checks, not a service directory, so each alert meets every check its service logged, and some services log from two regions. Skip that and the 688 at the top of the preview looks like a bug when the log holds only 200 alerts; 'fix' it with a dedup and you ship a different heatmap from the one on-call reads.

Getting there

Step 1: Find where region lives

alert_events carries svc_name and nothing geographic. The only region column is on svc_health, keyed by the same svc_name, so the service name is the bridge. A per-service count of alert_events is a dead end: there is nothing to roll it up by.

Step 2: Join on svc_name and accept what drops

An inner join keeps only services that have health checks. 'db-primary' and 'cache-01' fire 48 alerts between them but never appear in svc_health, so they have no region and leave the map. That is the intended behavior: a heatmap cannot place what it cannot locate.

Step 3: Name what one joined row is

After the join, a row is an alert paired with one health check of its service, not an alert. Every matched service has 18 checks: 'auth-svc' logs 10 from 'us-east-1' and 8 from 'eu-west-1', so its 24 alerts become 240 rows in one region and 192 in the other. The 152 matched alerts turn into 2,736 rows, exactly the sum of the preview.

Step 4: Tally per region, noisiest first

COUNT(*) grouped by sh.region counts those pairings. Sort by alert_count DESC, then by region: three regions tie at 468, and the alphabetical key puts 'ap-south-1' ahead of 'eu-central-1' and 'us-west-2'.

Alerts per region, one row per alert-check pairing
SELECT sh.region, COUNT(*) AS alert_count
FROM alert_events ae
INNER
JOIN svc_health sh ON ae.svc_name = sh.svc_name
GROUP BY sh.region
ORDER BY alert_count DESC, region
One row per health check
Joining svc_health as it is weights each alert by how many checks its service logged in a region: 'us-east-1' reads 688.
One row per service and region
Collapsing to unique (svc_name, region) pairs first counts an alert once per region: 'us-east-1' reads 74. Same GROUP BY, different question.

Common follow-up questions