Auth Endpoints

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
City and County of San Francisco

Problem

Our credential submissions are the POST requests to the auth routes in api_calls, and clients log the HTTP method in whatever case they send it, so 'post' and 'POST' are the same request. Pull those calls with their latency in the order they were logged, earliest first.

Not all endpoints are visible to everyone.

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 17 rows
call_timeendpointmethodlatency
2023-01-23 22:46:00/api/v1/auth/loginPOST381.6
2024-03-07 10:22:00/api/v1/auth/loginpost589.2
2024-09-02 05:17:00/api/v1/auth/logoutPOST502.7
2024-10-14 17:53:00/api/v1/auth/logoutpost710.3
2025-11-09 16:52:00/api/v1/auth/loginPOST1108.2

Worked solution and explanation

What this really is

This is a dirty-categorical filter dressed up as a security review. The route side is the easy half: endpoint LIKE '%auth%' catches both /api/v1/auth/login and /api/v1/auth/logout. The real test is method, which this log stores as 'POST' for most clients and 'post' for some. Anyone who writes method = 'POST' gets 11 rows, sees uppercase in the result and ships it. Six of the 17 credential submissions are gone, and nothing in the output tells you.

Getting there

Step 1: Profile method before filtering on it

Before trusting a literal, look at what the column holds. method has seven spellings for five verbs: 'get' and 'post' sit beside 'GET' and 'POST'. That one look decides the whole query.

Step 2: Fold the case, then compare

LOWER(method) = 'post' collapses both spellings into one value. You fold the column, not the literal: the literal is already in the case you chose.

Step 3: Match the route by substring

Auth traffic is split across login and logout, so equality against either path loses the other. endpoint LIKE '%auth%' takes both. On this table the paths are already lowercase, so the route side needs no folding.

Step 4: Sort by call_time

'Earliest first' means call_time. call_id is not assigned in time order here, so sorting by it scrambles the timeline the reviewer reads.

POST submissions to auth routes, as a timeline
SELECT call_time, endpoint, method, latency
FROM api_calls
WHERE endpoint LIKE '%auth%'
  AND LOWER(method) = 'post'
ORDER BY call_time
The version that looks right
SELECT call_time, endpoint, method, latency
FROM api_calls
WHERE endpoint LIKE '%auth%'
  AND method = 'POST'
ORDER BY call_time

Returns 11 of the 17 rows: every lowercase 'post' submission is dropped.

Literal as written
method = 'POST' returns 11 rows and drops the 6 logged as 'post'. The output is still tidy, which is exactly why nobody catches it.
Case folded
LOWER(method) = 'post' returns all 17. The method column in the result still shows both spellings, because the folding happens only in the filter.

Common follow-up questions