Latency Variance and Std Dev
An easy SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- easy
- Seniority
- L3
- Asked in
- Vesta Innovations
Problem
For successful API calls (status 200), compute the mean latency, variance, and standard deviation.
How much does latency actually vary?
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
| mean_latency | variance_latency | stddev_latency |
|---|---|---|
| 226.2494140625 | 45607.50585903168 | 213.5591390201592 |
Worked solution and explanation
What this is really asking
This is a population-moments problem dressed up as a latency report. The mean is the free third; the skill is building the variance out of the only two averages you can always compute, AVG(latency) and AVG(latency * latency), because Var = E[X^2] - E[X]^2 needs nothing else. The trap is habit: reaching for a variance aggregate the engine does not give you, or dividing by N-1. The first gets you no number at all. The second lands about 89 above the expected 45,607.5, and the grade checks three decimals.
Step 1: Keep only status = 200
The sample rows already show 201 and 204, which look like success but are not the slice being measured. Filter on the exact code, once, so every moment comes from the same set of calls.
Step 2: Take both averages in one pass
AVG(latency) and AVG(latency * latency) sit side by side in one SELECT. Six status 200 rows have a NULL latency; both averages skip them, so both are over the same 512 calls and the identity stays honest.
Step 3: Subtract, then take the root
AVG(latency * latency) - AVG(latency) * AVG(latency) is the population variance: AVG already divides by N, so the divisor is decided for you. SQRT of that same expression is the standard deviation. The engine keeps one accumulator per distinct aggregate, so writing the expression twice costs no extra work, only care: the two copies must match exactly.
SELECT
AVG(latency) AS mean_latency,
AVG(latency * latency) - AVG(latency) * AVG(latency) AS variance_latency,
SQRT(AVG(latency * latency) - AVG(latency) * AVG(latency)) AS stddev_latency
FROM api_calls
WHERE status = 200;- Three scalar subqueries
- Each output column gets its own
(SELECT ... FROM api_calls WHERE status = 200). That is three scans of the table and three copies of the filter. If one copy drifts, the stddev stops describing the same calls as the mean. - One SELECT, one filter
- One
WHERE status = 200feeds every aggregate, soapi_callsis read once and both averages accumulate in the same pass. The variance and the stddev are just arithmetic on those two numbers.
Common follow-up questions
Common follow-up questions
- Should this be population or sample variance? (Population (divide by N) treats the 200s as the whole world. Sample (N-1) treats them as a draw from a larger distribution. For a slice of production traffic you already hold in full, population is usually right.)
- How would you report variance per endpoint and still scan once? (Add `TRIM(endpoint)` to the select list and group by it; every average and the `SQRT` carry over row by row, still in one scan. The padded `endpoint` values are why the `TRIM` is there.)
- Latency is heavy-tailed. Is the mean even the right metric? (Usually not on its own. Tail SLOs read p95 or p99, and a few timeouts can inflate both the mean and the stddev without moving the typical call.)