Unused Read Tokens
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
- Airbnb
Problem
Only a token that has served zero requests counts as never used, whatever its last-used timestamp says. Before we purge them, how many tokens in api_tokens whose scope is exactly 'read' (the narrower 'read:...' sub-scopes stay out) fall into that bucket?
Active tokens that nobody uses.
Tables
api_tokens
Columns: token_id INTEGER, owner_id INTEGER, scope TEXT, status TEXT, issued TEXT, expires TEXT, last_used TEXT, requests INTEGER
| token_id | owner_id | scope | status | issued | expires | last_used | requests |
|---|---|---|---|---|---|---|---|
| 1043 | 197 | write | revoked | 2025-02-02 | 2027-02-02 | 2026-02-02 | 47 |
| 1086 | 294 | admin | expired | 2025-03-03 | 2027-03-03 | 2026-03-03 | 94 |
| 1129 | 391 | read:users | Active | 2025-04-04 | 2027-04-04 | 2026-04-04 | 141 |
| 1172 | 488 | write:orders | REVOKED | 2025-05-05 | 2027-05-05 | 2026-05-05 | 188 |
| 1215 | 585 | read:analytics | active | 2025-06-06 | 2027-06-06 | 2026-06-06 | 235 |
| 1258 | 682 | full_access | revoked | 2025-07-07 | NULL | 2026-07-07 | 282 |
| 1301 | 779 | read | expired | 2025-08-08 | 2027-08-08 | 2026-08-08 | 329 |
| 1344 | 876 | write | Active | 2025-09-09 | 2027-09-09 | NULL | 0 |
| 1387 | 973 | admin | REVOKED | 2025-10-10 | 2027-10-10 | 2026-01-10 | 423 |
| 1430 | 1070 | read:users | active | 2025-11-11 | 2027-11-11 | 2026-02-11 | 470 |
Expected output
| unused_read_tokens |
|---|
| 2 |
Worked solution and explanation
The field that lies
This is a two-predicate filter wearing a security costume, and the whole question is which column means 'never used'. Everyone gets the scope filter right. What separates candidates is reaching for requests = 0 instead of the tempting last_used IS NULL. On this table last_used is missing for about 15% of rows for its own reasons, while requests is never NULL. The two sets overlap but are not the same, so the NULL proxy purges tokens that are actively serving traffic and leaves behind idle ones that happen to carry a timestamp.
Step 1: Scope to plain read tokens
scope = 'read' is an exact match. The narrower 'read:...' sub-scopes are separate credentials with their own cleanup, so a prefix match would inflate the purge.
Step 2: Define 'never used' by the counter
requests = 0 is the activity record itself. last_used is a side effect that can fail to be written, so it is evidence, not the definition.
Step 3: Count what survives both filters
The ask is how big the purge is, so COUNT(*) over the filtered rows answers it in one number named unused_read_tokens.
SELECT COUNT(*) AS unused_read_tokens
FROM api_tokens
WHERE scope = 'read'
AND requests = 0- Timestamp proxy
WHERE scope = 'read' AND last_used IS NULLcounts tokens whose timestamp was never written, including ones with thousands ofrequests.- Request counter
WHERE scope = 'read' AND requests = 0counts exactly the tokens that never served a call, regardless oflast_used.
Common follow-up questions
- How would you list the idle read tokens per `owner_id` so owners can be notified? (Moves from a count to a grouped breakdown.)
- How would you also require the token to be past its `expires` date? (Adds `expires < CURRENT_DATE`, and forces a decision about NULL `expires`.)
- How many tokens disagree between `last_used IS NULL` and `requests = 0`? (Quantifies the trap and is a natural data-quality check.)