Everyone Counts
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
- Philips
Problem
The growth team is profiling last year's signups that are still waiting to verify their accounts, broken out by age band. Plenty of those accounts have no band on file, and the team wants them kept as a segment of their own rather than dropped. Show the bands that pulled in more than four of these signups along with how many landed in each.
Every account lands in a segment, even the ones with nothing on file.
Tables
users
Columns: user_id INTEGER, username TEXT, email TEXT, signup_date TEXT, account_status TEXT, age_bucket TEXT
| user_id | username | signup_date | account_status | age_bucket | |
|---|---|---|---|---|---|
| 100 | alice | alice@example.com | 2025-02-02 | inactive | 25-34 |
| 197 | aaron42 | aaron42@example.com | 2026-03-03 | suspended | 35-44 |
| 294 | amelia | amelia@example.com | 2024-04-04 | pending_verification | 45-54 |
| 391 | arjun | arjun@example.com | 2025-05-05 | active | 55-64 |
| 488 | ava99 | ava99@example.com | 2026-06-06 | inactive | 65+ |
| 585 | andrew_k | andrew_k@example.com | 2024-07-07 | suspended | NULL |
| 682 | anika | anika@example.com | 2025-08-08 16:34:00 | pending_verification | 18-24 |
| 779 | aiden | aiden@example.com | 2026-09-09 14:53:00 | active | 25-34 |
| 876 | aria_b | aria_b@example.com | 2024-10-10 | inactive | 35-44 |
| 973 | brian | brian@example.com | 2025-11-11 | suspended | 45-54 |
Expected output
| age_bucket | user_count |
|---|---|
| NULL | 5 |
| 18-24 | 6 |
| 45-54 | 5 |
| 55-64 | 5 |
Worked solution and explanation
The reflex this problem is really testing
Underneath the demographics story, this is a grouped head count with a cutoff no single row can see, plus a segment everyone forgets to keep. You scope to last year's unverified signups with WHERE account_status = 'pending_verification' AND signup_date LIKE '2025-%'. 'More than four signups in the band' is a fact about a band, though, and a band's size only exists after GROUP BY age_bucket, so that test sits in HAVING. The real trap is the accounts with no age on file: they are a band too. Reach for WHERE age_bucket IS NOT NULL on autopilot, or count with COUNT(age_bucket), and the no-band segment silently disappears even though it clears the bar with five signups. Four rows become three, and the one you lost is the one the growth team asked you to keep.
Break down the requirements
Step 1: Scope to last year's pending signups, then group
WHERE account_status = 'pending_verification' AND signup_date LIKE '2025-%' keeps only last year's still-unverified signups. The prefix match also catches the odd signup_date stored with a time of day. GROUP BY age_bucket then collapses these signups into one row per band. Every account with no age on file falls into a single null group, and that group is exactly the no-band segment the team asked to keep, so there is no filter on age_bucket at all.
Step 2: Filter bands by size after grouping
The cutoff is a per-band count, which only exists once the bands exist, so it lives in HAVING COUNT(*) > 4. A row-level WHERE runs before grouping and has no band size to test. The cutoff drops the three thin bands sitting at four ('25-34', '35-44', '65+') and keeps the bands that actually drew a crowd.
Step 3: Count rows, not labels
COUNT(*) counts every account in the group, the null group's included. COUNT(age_bucket) counts only non-null labels, so the no-band group would report zero and fail the cutoff with five accounts sitting in it. The metric is a head count, so count rows.
The solution
SELECT age_bucket, COUNT(*) AS user_count
FROM users
WHERE account_status = 'pending_verification' AND signup_date LIKE '2025-%'
GROUP BY age_bucket
HAVING COUNT(*) > 4Common follow-up questions
- How would you show each band's share of last year's unverified signups? (Tests a window over the grouped counts: `100.0 * COUNT(*) / SUM(COUNT(*)) OVER ()`.)
- What changes if the cutoff were the average band size instead of a fixed four? (Tests swapping the constant in `HAVING` for a scalar subquery over the grouped counts.)
- How would you label the no-band segment on a dashboard? (Tests presentation of the null group, e.g. `COALESCE(age_bucket, 'Unknown')`.)