The Loudest in the Room

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
TripAdvisor

Problem

A wave of credential-stuffing has been hammering the login endpoint, and the gateway team wants its two worst offenders. From the rate_limits log, look only at requests to /api/v1/auth and find the two clients with the highest total blocked requests, most blocked first.

Somewhere in the noise, two keep knocking. Find who.

Tables

rate_limits

Columns: limit_id INTEGER, client TEXT, endpoint TEXT, allowed INTEGER, blocked INTEGER, window TEXT, checked TEXT

rate_limits: 10 of 200 rows
limit_idclientendpointallowedblockedwindowchecked
129client-002/api/v1/search107NULL5m2026-02-02 01:03:00
158client-003/api/v1/orders114NULL15m2026-03-03 02:06:00
187mobile-app/api/v2/analytics121NULL1h2026-04-04 03:09:00
216partner-api/api/v1/auth128121d2026-05-05 04:12:00
245internal-svc/api/v1/payments135NULL1m2026-06-06 05:15:00
274batch-worker/api/v1/users142NULL5m2026-07-07 06:18:00
303admin-ui/api/v1/search149NULL15m2026-08-08 07:21:00
332webhook-svc/api/v1/orders156241h2026-09-09 08:24:00
361client-010/api/v2/analytics163NULL1d2026-01-10 09:27:00
390client-001/api/v1/auth170NULL1m2026-02-11 10:30:00

Expected output

First 2 of 2 rows
clienttotal_blocked
client-001840
webhook-svc696

Worked solution and explanation

Strip the abuse-detection costume off this and it's a per-client SUM over a magnitude column, scoped to a single endpoint, with a top-two stapled on. The skill being probed: do you know blocked is the number to add up, not a flag to count? Almost everyone gets the GROUP BY and the LIMIT. What separates people is realizing blocked holds a per-window count, so COUNT(*) and SUM(blocked) answer different questions and rank the clients differently. Count rows and you report which client showed up most on the login endpoint; sum the magnitudes and you report who actually got blocked the most.

The trap: a row is not a request

On /api/v1/auth, blocked is populated only on the windows where something was actually blocked, and each value is how many requests that window rejected. There are two independent ways to get this wrong. First, COUNT(*) per client counts appearances on the endpoint, which is dominated by rows where nothing was blocked and answers a different question. Second, even a count restricted to blocked rows tallies how many windows a client was blocked in, not how many requests. Only SUM(blocked) adds up the actual rejected-request volume, and on this data it swaps the second-place client versus a plain row count.

Ranks appearances, not blocks
COUNT(*) per client on the endpoint counts rows. The many windows where blocked is null or zero dominate, so the 'top' clients are just the chattiest. Here it hands second place to 'partner-api'.
Ranks actual blocked volume
SUM(blocked) adds the per-window magnitudes, so a client with a few high-count windows outranks one with many tiny ones. Here it correctly gives second place to 'webhook-svc'.

How to get there

Step 1: Scope to the endpoint under attack

WHERE endpoint = '/api/v1/auth' runs per row, before grouping, so only login traffic ever enters a client's total. Drop it and you rank blocks across every endpoint, which is a different question and a different top two. This filter is exactly what makes 'webhook-svc' beat 'batch-worker' for second place.

Step 2: Collapse to one row per client

GROUP BY client sets the grain to the answer's grain, and SUM(blocked) adds the per-window magnitudes within each client. Everything in the SELECT is now the grouping key or an aggregate over it. If you find yourself sorting raw rows instead, that's the tell you skipped this step.

Step 3: Rank and take the top two

ORDER BY total_blocked DESC puts the worst offenders on top and LIMIT 2 keeps the two you were asked for. The two totals here (840 and 696) are distinct, so the top two are unambiguous and the engine can keep a small top-two heap rather than materializing a full sort.

Canonical solution
SELECT client, SUM(blocked) AS total_blocked
FROM rate_limits
WHERE endpoint = '/api/v1/auth'
GROUP BY client
ORDER BY total_blocked DESC
LIMIT 2

Scope to the login endpoint, sum the block magnitudes per client, take the worst two.

Common follow-up questions