Ad Clickers
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
- L4
Problem
Which users clicked at least one ad in the ad_impressions log, and how much revenue did those clicks bring in? Show each clicker's username from users alongside their total click revenue, highest contributor first.
Who clicked? What did they spend?
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 |
ad_impressions
Columns: impression_id INTEGER, user_id INTEGER, ad_campaign TEXT, impression_time TEXT, clicked INTEGER, revenue REAL
| impression_id | user_id | ad_campaign | impression_time | clicked | revenue |
|---|---|---|---|---|---|
| 253 | 100 | BRAND_AWARENESS_Q1 | 2026-02-02 01:07:00 | 0 | NULL |
| 306 | 197 | PRODUCT_LAUNCH_X | 2026-03-03 02:14:00 | 0 | NULL |
| 359 | 294 | RETARGETING_CART | 2026-04-04 03:21:00 | 0 | NULL |
| 412 | 391 | HOLIDAY_PROMO | 2026-05-05 04:28:00 | 0 | NULL |
| 465 | 488 | NEW_USER_ACQU | 2026-06-06 05:35:00 | 1 | 0.2 |
| 518 | NULL | LOYALTY_PROGRAM | 2026-07-07 06:42:00 | 0 | NULL |
| 571 | 682 | FLASH_SALE_48H | 2026-08-08 07:49:00 | 0 | NULL |
| 624 | 779 | SUMMER_SALE_2024 | 2026-09-09 08:56:00 | 0 | NULL |
| 677 | 876 | BRAND_AWARENESS_Q1 | 2026-01-10 09:03:00 | 0 | NULL |
| 730 | 973 | PRODUCT_LAUNCH_X | 2026-02-11 10:10:00 | 1 | 0.35 |
Expected output
| username | total_revenue |
|---|---|
| emma | 25.1 |
| dora_77 | 23.55 |
| dahlia | 22.5 |
| dimitri | 22 |
| devon | 21 |
Worked solution and explanation
Why this problem exists in real interviews
Join a dimension to a filtered fact and sum a metric: this is the most common shape of revenue analytics in the wild. Total purchase value by customer, total click revenue by ad, total session minutes by user. Interviewers use this prompt to check whether you place the filter in WHERE (correct) or in HAVING (wrong scope), whether you join on the right key, and whether you sort by the aggregate with a deterministic tiebreak.
Break down the requirements
Step 1: Join users to ad_impressions on user_id
The dimension (username) lives on users; the fact (revenue, clicked flag) lives on ad_impressions. INNER JOIN on user_id keeps only users who had at least one impression. Users with no impressions should not appear, so INNER is correct.
Step 2: Filter to clicked = 1 in WHERE
WHERE a.clicked = 1 drops unclicked impressions before grouping. This belongs in WHERE because it filters rows, not groups. A user with zero clicks is eliminated at the row level and therefore absent from the grouped output, satisfying 'at least one click.'
Step 3: Group by username, sum revenue
GROUP BY u.username produces one row per user. SUM(a.revenue) totals click revenue. If username is not unique (rare, but possible), GROUP BY user_id is safer; the spec asks for username in the output, so group by username if it is unique by contract.
Step 4: Sort by total_revenue DESC, username ASC
ORDER BY total_revenue DESC, username ASC. The tiebreak on username ASC makes output deterministic for users with identical totals (common when revenue is always the same small set of values).
The solution
SELECT
u.username,
SUM(a.revenue) AS total_revenue
FROM users u
JOIN ad_impressions a ON u.user_id = a.user_id
WHERE a.clicked = 1
GROUP BY u.username
ORDER BY total_revenue DESC, u.username ASCCommon follow-up questions
- How would you also return the number of clicks alongside the revenue? (add COUNT(*) AS `click_count` to the SELECT. Explain why COUNT(*) works after the WHERE filter.)
- What changes if you need top 5 clickers only? (add LIMIT 5 after the ORDER BY. Discuss how the ORDER BY tiebreak interacts with LIMIT (the tiebreak is why the output is deterministic across runs).)
- How would you handle a user who has clicks but every click has revenue = NULL? (SUM returns NULL for all-NULL groups. The user appears with `total_revenue` NULL, which may violate 'at least one click' in spirit; discuss COALESCE(SUM(revenue), 0) or HAVING SUM(revenue) IS NOT NULL.)