Services With Most Checks in 2025
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
Problem
For each service in svc_health, which year had the most checks come back anything other than healthy, how many were there, and in which month did that year's last troubled check land? Our status values were logged with inconsistent casing, so 'Healthy' and 'healthy' are the same outcome, and when two years tie for a service's worst we want both.
Last year's most-checked services.
Tables
svc_health
Columns: check_id INTEGER, svc_name TEXT, status TEXT, latency REAL, uptime REAL, checked TEXT, region TEXT
| check_id | svc_name | status | latency | uptime | checked | region |
|---|---|---|---|---|---|---|
| 137 | payment-api | degraded | 3.6 | 97.03 | 2026-02-02 01:05:00 | us-west-2 |
| 174 | user-svc | unhealthy | 6.7 | 94.06 | 2026-03-03 02:10:00 | eu-west-1 |
| 211 | search-api | timeout | 9.8 | 91.09 | 2026-04-04 03:15:00 | ap-south-1 |
| 248 | gateway | Healthy | 12.9 | 98.12 | 2026-05-05 04:20:00 | eu-central-1 |
| 285 | notif-svc | DEGRADED | 16 | 95.15 | 2026-06-06 05:25:00 | us-east-1 |
| 322 | ml-serving | healthy | 19.1 | 92.18 | 2026-07-07 06:30:00 | us-west-2 |
| 359 | cache-svc | degraded | NULL | 99.21 | 2026-08-08 07:35:00 | eu-west-1 |
| 396 | queue-worker | unhealthy | 25.3 | 96.24 | 2026-09-09 08:40:00 | ap-south-1 |
| 433 | cron-svc | timeout | 28.4 | 93.27 | 2026-01-10 09:45:00 | eu-central-1 |
| 470 | auth-svc | Healthy | 31.5 | 90.3 | 2026-02-11 10:50:00 | us-east-1 |
Expected output
| svc_name | check_year | check_count | last_trouble_month |
|---|---|---|---|
| auth-svc | 2026 | 11 | 2026-09 |
| gateway | 2025 | 8 | 2025-12 |
| ml-serving | 2022 | 8 | 2022-12 |
| queue-worker | 2024 | 8 | 2024-12 |
| user-svc | 2023 | 8 | 2023-12 |
Worked solution and explanation
This is a per-group argmax wearing an incident-review costume. Everyone can count troubled checks per service per year. What separates candidates is getting the year back out: MAX(check_count) tells you how bad the worst year was, never which year it was or when its last trouble landed. The second trap is upstream: status was logged in mixed case, so a filter on the raw text counts every 'Healthy' row as trouble and quietly inflates the very counts you are comparing.
The trap
- Looks right
WHERE status <> 'healthy'keeps 'Healthy' rows, so they are counted as trouble and can move a service's worst year.- Is right
WHERE LOWER(status) <> 'healthy'folds every spelling into one outcome before the filter runs.
Building it
Step 1: Filter the troubled checks, case-blind
LOWER(status) <> 'healthy' keeps degraded, unhealthy and timeout checks in any casing. A null latency or uptime does not matter: the row is still a check.
Step 2: Bucket by year in trouble_counts
Group on svc_name and DATE_TRUNC('year', checked). In the same pass take COUNT(*) for the size of the bucket and MAX(checked) for its last troubled check, so the detail you need later rides along with the bucket instead of costing a second scan.
Step 3: Find each service's own peak in worst_year
Aggregate trouble_counts again by svc_name with MAX(check_count). This is the number, with the year stripped off.
Step 4: Join the buckets back to their peak
Join on svc_name and check_count = max_count. The join recovers the year, and because it is an equality, two years sharing the peak both survive. Only now turn the bucket into readable values: EXTRACT(YEAR FROM year_start) for the year and TO_CHAR(last_trouble_at, 'YYYY-MM') for the month.
WITH trouble_counts AS (
SELECT svc_name,
DATE_TRUNC('year', checked) AS year_start,
COUNT(*) AS check_count,
MAX(checked) AS last_trouble_at
FROM svc_health
WHERE LOWER(status) <> 'healthy'
GROUP BY svc_name, DATE_TRUNC('year', checked)
),
worst_year AS (
SELECT svc_name, MAX(check_count) AS max_count
FROM trouble_counts
GROUP BY svc_name
)
SELECT tc.svc_name,
EXTRACT(YEAR FROM tc.year_start) AS check_year,
tc.check_count,
TO_CHAR(tc.last_trouble_at, 'YYYY-MM') AS last_trouble_month
FROM trouble_counts tc
JOIN worst_year wy
ON tc.svc_name = wy.svc_name
AND tc.check_count = wy.max_countOne CTE per intermediate result; the join scopes the peak to the service and keeps ties.
Common follow-up questions
- How would you return only one year per service, picking the most recent when two years tie? (Tests adding a deterministic tie rule on top of the peak.)
- What changes if the worst period should be a calendar month instead of a year? (Swapping `DATE_TRUNC('year', checked)` for a month bucket and checking the output labels still make sense.)
- How would you show each worst year's share of troubled checks out of all its checks? (Needs a second count over every check, not just the filtered ones.)