One-Track Mind

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

Problem

We're profiling read-only API users: people who have only ever called the API with GET and never any other method. For each endpoint, count these read-only users, and return the endpoint or endpoints with the highest count, listing them in alphabetical order by endpoint.

Some users only ever read, never write. Find where the read-only crowd gathers.

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 3 of 3 rows
endpointget_only_users
/api/v1/orders24
/api/v1/users24
/api/v2/products24

Worked solution and explanation

What this problem really is

This is a per-user set difference wearing an API-analytics costume. The real question: can you count the users who did GET and nothing but GET, broken down by endpoint? Anyone can write WHERE method = 'GET' and group by endpoint. The trap is that a plain GET filter counts every user who ever made a GET, including the ones who also POST, PUT and DELETE all day. A row filter cannot express 'has GET and has no non-GET'; you need an exclusion against the set of users who ever did something else. Skip that and your 'read-only' population balloons with users who are anything but, and every endpoint count comes out inflated.


Break down the requirements

Step 1: Identify who is NOT read-only (case-insensitive)

Build the exclusion set: every user_id that ever made a non-GET call (UPPER(method) <> 'GET', non-null user_id). Any user in this set is disqualified, no matter how many GETs they also made. Normalize with UPPER so 'get' and 'GET' collapse together.

Step 2: Keep only the read-only users' GET rows

Scan the GET rows (UPPER(method) = 'GET') and drop anyone in the exclusion set with user_id NOT IN (...). A GET row with no user_id falls out on its own: NULL NOT IN (...) is unknown, and COUNT(DISTINCT user_id) would skip it anyway, so this side needs no second guard.

Step 3: Take the top endpoints, ties and all

GROUP BY endpoint counting unique users, then keep every endpoint whose count equals (SELECT MAX(get_only_users) FROM counts) and order by endpoint. Using ORDER BY ... LIMIT 1 here silently drops tied endpoints, and the seed has a three-way tie at 24.


The solution

GET-only users via set exclusion, then the top endpoints
WITH counts AS (
  SELECT endpoint,
         COUNT(DISTINCT user_id) AS get_only_users
  FROM api_calls
  WHERE UPPER(method) = 'GET'
    AND user_id NOT IN (
      SELECT DISTINCT user_id
      FROM api_calls
      WHERE UPPER(method) <> 'GET'
        AND user_id IS NOT NULL
    )
  GROUP BY endpoint
)
SELECT endpoint, get_only_users
FROM counts
WHERE get_only_users = (SELECT MAX(get_only_users) FROM counts)
ORDER BY endpoint

Common follow-up questions