The Long Tail

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
L4
Asked in
General Assembly

Problem

Some calls in api_calls were never timed and carry no latency, and the HTTP method was logged in inconsistent casing, so 'get' and 'GET' are the same verb. An on-call engineer setting a best-case baseline wants each method's average latency across its five fastest timed calls, rounded to two decimals.

Averages hide the pain. The tail is where SLOs live.

Tables

api_calls

Columns: call_id INTEGER, endpoint TEXT, method TEXT, status INTEGER, latency REAL, user_id INTEGER, call_time TEXT, err_msg TEXT

api_calls: 10 of 670 rows
call_idendpointmethodstatuslatencyuser_idcall_timeerr_msg
8079/api/v1/ordersPOST2004.21002026-02-02 01:07:00NULL
8158/api/v2/productsPUT2007.91972026-03-03 02:14:00NULL
8237/api/v1/searchDELETE20111.62942026-04-04 03:21:00NULL
8316/api/v1/auth/loginPATCH20415.33912026-05-05 04:28:00NULL
8395/api/v1/auth/logoutget30119NULL2026-06-06 05:35:00NULL
8474/api/v2/analyticspost40022.75852026-07-07 06:42:00Not found
8553/api/v1/paymentsGET40126.46822026-08-08 07:49:00Unauthorized
8632/api/v1/users/POST40330.17792026-09-09 08:56:00Rate limit exceeded
8711/api/v2/products/PUT40433.88762026-01-10 09:03:00Internal server error
8790/api/v1/usersDELETE50037.5NULL2026-02-11 10:10:00Bad gateway

Expected output

First 5 of 5 rows
methodfastest_five_avg
DELETE50.94
GET8.2
PATCH57.36
POST24.06
PUT44.52

Worked solution and explanation

What this really is

Behind the baseline language this is a bottom-five-per-group average over a dirty log. Anyone can number rows inside a method. What separates candidates is noticing that the missing latencies do not politely disappear. Number calls fastest first and an untimed call sorts to the very front of its method, quietly taking slot one. The average then skips it, so you report the mean of four calls while believing it is five. Skip the case fold as well and 'get' and 'post' become phantom methods, each holding a fraction of the real traffic.


Building it

Step 1: Fold the casing and drop the untimed calls

Uppercase method so 'get' and 'GET' land in one bucket, and keep only rows where latency IS NOT NULL. Every later step reads this cleaned set.

Step 2: Number each method's calls fastest first

ROW_NUMBER() OVER (PARTITION BY UPPER(method) ORDER BY latency) gives each method's fastest timed call position 1. Because the NULLs are already gone, positions 1 through 5 are five real measurements.

Step 3: Keep five and average them

Filter to speed_rank <= 5, then GROUP BY method and take ROUND(AVG(latency), 2). A method with fewer than five timed calls simply averages what it has.


The solution

Five fastest timed calls per method
WITH timed_calls AS (
    SELECT
        UPPER(method) AS method,
        latency,
        ROW_NUMBER() OVER (PARTITION BY UPPER(method) ORDER BY latency) AS speed_rank
    FROM api_calls
    WHERE latency IS NOT NULL
)
SELECT
    method,
    ROUND(AVG(latency), 2) AS fastest_five_avg
FROM timed_calls
WHERE speed_rank <= 5
GROUP BY method;
No filter on missing latency
An untimed call sorts ahead of every real one, takes position 1, and AVG(latency) ignores it. The method's baseline is the mean of four calls, and nothing in the output says so.
Filter first, then number
WHERE latency IS NOT NULL runs before ROW_NUMBER(), so positions 1 to 5 are always measured calls and the average covers exactly the five fastest.

Common follow-up questions