First Contact
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
- Uber
Problem
We're measuring first-impression latency from api_calls: for the first call each user makes to a given endpoint, how long it took in whole milliseconds with the timer's fractional digits dropped, where calls with no user attached all count as one anonymous visitor. The log sometimes records an endpoint with stray spaces or a trailing slash, and those spellings are the same endpoint. Report the average first-contact latency for every endpoint, slowest first.
Every user's first hello to an endpoint. Timed.
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
| endpoint | avg_initial_call_latency |
|---|---|
| /api/v1/auth/logout | 402.76190476190476 |
| /api/v1/search | 383.1014492753623 |
| /api/v1/auth/login | 354.6271186440678 |
| /api/v1/payments | 276.6 |
| /api/v2/products | 272.18 |
Worked solution and explanation
What this is really asking
This is a per-group first-occurrence pick wearing a latency-report costume. For every (user, route) pair you want exactly one row, the earliest call, and only then do you average within each route. The trap is AVG(latency) GROUP BY endpoint straight off the raw table. That answers 'average latency per endpoint', a different number dominated by whoever calls the most. Miss the dedup and your first-impression metric quietly becomes a traffic-weighted average of everyone's repeat traffic.
Break down the requirements
Step 1: Fold the endpoint spellings into one route
The log writes one route several ways: '/api/v1/orders ' with a trailing space, '/api/v1/users/' with a trailing slash. RTRIM(TRIM(endpoint), '/') strips the padding and then the slash. Clean the key first, because everything downstream partitions and groups on it.
Step 2: Number each user's calls to each route by time
ROW_NUMBER() OVER (PARTITION BY user_id, RTRIM(TRIM(endpoint), '/') ORDER BY call_time). Partitioning by user and cleaned route gives one earliest row per pair, so a chatty user contributes a single first contact per route. NULL user_id rows share one partition, which is exactly the one anonymous visitor the statement describes.
Step 3: Keep only each user's first call per route
Filter on rn = 1 in an outer query. A window function cannot live in WHERE, so the derived table is mandatory, not stylistic. This is the row set of genuine first impressions.
Step 4: Floor each latency, average per route, and order
AVG(CAST(latency AS INTEGER)) floors every surviving latency to a whole millisecond before averaging. Grouping by the cleaned endpoint gives one number per route, and sorting that average descending puts the slowest first impressions on top.
The solution
SELECT endpoint,
AVG(CAST(latency AS INTEGER)) AS avg_initial_call_latency
FROM (
SELECT RTRIM(TRIM(endpoint), '/') AS endpoint,
latency,
ROW_NUMBER() OVER (
PARTITION BY user_id, RTRIM(TRIM(endpoint), '/')
ORDER BY call_time
) AS rn
FROM api_calls
) first_calls
WHERE rn = 1
GROUP BY endpoint
ORDER BY avg_initial_call_latency DESCCOMMON FOLLOW-UP QUESTIONS
Common follow-up questions
- How would you report the median first-contact latency per endpoint instead of the average? (Keep the `rn = 1` set and apply `PERCENTILE_CONT(0.5)` per route, since an average hides tail behavior on latency.)
- What if you wanted the average over each user's first three calls to an endpoint? (Change the filter to `rn <= 3`. The partition definition stays the same.)
- How would you make this incremental for a daily job? (Fix the spellings at ingestion, persist each (user, route) first `call_time` once, then each day add only pairs seen for the first time instead of re-ranking 400M rows.)