Peak Hour Power Callers
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
- Redfin
Problem
Afternoon API traffic between 3 and 6 PM keeps spiking, and the on-call support engineer wants to know which callers in api_calls are driving it. List each user with at least 3 calls in that window and their call count, busiest first, with calls that carry no user id counted together as a single caller.
One hour. The phone lines exploded.
Tables
api_calls
Columns: call_id INTEGER, endpoint TEXT, method TEXT, status INTEGER, latency REAL, user_id INTEGER, call_time TEXT, err_msg TEXT
| call_id | endpoint | method | status | latency | user_id | call_time | err_msg |
|---|---|---|---|---|---|---|---|
| 8079 | /api/v1/orders | POST | 200 | 4.2 | 100 | 2026-02-02 01:07:00 | NULL |
| 8158 | /api/v2/products | PUT | 200 | 7.9 | 197 | 2026-03-03 02:14:00 | NULL |
| 8237 | /api/v1/search | DELETE | 201 | 11.6 | 294 | 2026-04-04 03:21:00 | NULL |
| 8316 | /api/v1/auth/login | PATCH | 204 | 15.3 | 391 | 2026-05-05 04:28:00 | NULL |
| 8395 | /api/v1/auth/logout | get | 301 | 19 | NULL | 2026-06-06 05:35:00 | NULL |
| 8474 | /api/v2/analytics | post | 400 | 22.7 | 585 | 2026-07-07 06:42:00 | Not found |
| 8553 | /api/v1/payments | GET | 401 | 26.4 | 682 | 2026-08-08 07:49:00 | Unauthorized |
| 8632 | /api/v1/users/ | POST | 403 | 30.1 | 779 | 2026-09-09 08:56:00 | Rate limit exceeded |
| 8711 | /api/v2/products/ | PUT | 404 | 33.8 | 876 | 2026-01-10 09:03:00 | Internal server error |
| 8790 | /api/v1/users | DELETE | 500 | 37.5 | NULL | 2026-02-11 10:10:00 | Bad gateway |
Expected output
| user_id | call_count |
|---|---|
| 391 | 4 |
| 3786 | 3 |
| 1555 | 3 |
| NULL | 3 |
Worked solution and explanation
What this really is
Strip the costume and this is a per-caller frequency count fenced inside a recurring time-of-day window. The window is what bites first: call_time is a full timestamp spread over years of days, so any plain range on it selects one stretch of the calendar, not the same 3 to 6 PM slice on every day. Only the hour read out of each row isolates the afternoon. After that, the skill is keeping two filters apart: the window filters calls, and the 'at least 3' test filters callers. Read '3 to 6 PM' as hours 15 through 18 and you fold the whole 6 PM hour into every count.
The two-stage filter
Step 1: Fence the afternoon window
The window is a time of day, not a date range, so pull the hour out of call_time and keep 15, 16 and 17. EXTRACT(HOUR FROM call_time) BETWEEN 15 AND 17 captures 15:00:00 through 17:59:59 on every day in the table. Comparing call_time itself against '15:00:00' and '18:00:00' looks right and matches nothing, because every value starts with a date.
Step 2: Count per caller, keep the heavy hitters
Collapse the surviving rows to one row per caller with GROUP BY user_id, then keep the heavy hitters with HAVING COUNT(*) >= 3. Calls with no user_id fall into one group of their own, which the statement keeps as a single caller. COUNT(*) counts that group's rows; COUNT(user_id) would score it 0 and quietly drop it.
Step 3: Sort busiest first
Return the caller and the count with ORDER BY call_count DESC. Callers tied on a count can come back in any order: the question asks who is busiest, and nothing in it ranks two equally busy callers.
The solution
SELECT user_id, COUNT(*) AS call_count
FROM api_calls
WHERE EXTRACT(HOUR FROM call_time) BETWEEN 15 AND 17
GROUP BY user_id
HAVING COUNT(*) >= 3
ORDER BY call_count DESCCommon follow-up questions
- The output keeps calls with no `user_id` as one blank caller. If support wanted only identified users, what would you change, and in which clause? (Tests whether the candidate knows a NULL grouping key forms its own group and that excluding it is a row filter, not a group filter.)
- Why can the hour-of-day filter live in `WHERE` while the 3-call threshold cannot? (Tests understanding of pre-aggregation versus post-aggregation filtering.)
- The table is partitioned by day but you filter on the hour of day. What index or precomputation keeps this fast across all 200M rows? (Tests indexing intuition for a time-of-day predicate on a date-partitioned table.)