Users With and Without Ad Clicks

A hard SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.

Domain
SQL
Difficulty
hard
Seniority
L4
Asked in
Microsoft

Problem

Account reviews keep asserting that suspended and inactive accounts never engage with ads, yet nobody has checked that claim against the whole users base. For the growth team, split every user within each account status by whether they have ever clicked an ad in ad_impressions, with the number of users in each group.

Clicked an ad versus never clicked. The split.

Tables

users

Columns: user_id INTEGER, username TEXT, email TEXT, signup_date TEXT, account_status TEXT, age_bucket TEXT

users: 10 of 200 rows
user_idusernameemailsignup_dateaccount_statusage_bucket
100alicealice@example.com2025-02-02inactive25-34
197aaron42aaron42@example.com2026-03-03suspended35-44
294 ameliaamelia@example.com2024-04-04pending_verification45-54
391arjun arjun@example.com2025-05-05active55-64
488ava99ava99@example.com2026-06-06inactive65+
585andrew_kandrew_k@example.com2024-07-07suspendedNULL
682anikaanika@example.com2025-08-08 16:34:00pending_verification18-24
779aidenaiden@example.com2026-09-09 14:53:00active25-34
876aria_baria_b@example.com2024-10-10inactive35-44
973brianbrian@example.com2025-11-11suspended45-54

ad_impressions

Columns: impression_id INTEGER, user_id INTEGER, ad_campaign TEXT, impression_time TEXT, clicked INTEGER, revenue REAL

ad_impressions: 10 of 200 rows
impression_iduser_idad_campaignimpression_timeclickedrevenue
253100BRAND_AWARENESS_Q12026-02-02 01:07:000NULL
306197PRODUCT_LAUNCH_X2026-03-03 02:14:000NULL
359294RETARGETING_CART2026-04-04 03:21:000NULL
412391HOLIDAY_PROMO2026-05-05 04:28:000NULL
465488NEW_USER_ACQU2026-06-06 05:35:0010.2
518NULLLOYALTY_PROGRAM2026-07-07 06:42:000NULL
571682FLASH_SALE_48H2026-08-08 07:49:000NULL
624779SUMMER_SALE_20242026-09-09 08:56:000NULL
677876BRAND_AWARENESS_Q12026-01-10 09:03:000NULL
730973PRODUCT_LAUNCH_X2026-02-11 10:10:0010.35

Expected output

First 5 of 8 rows
account_statusclick_groupuser_count
activehas_click3
activeno_click47
inactivehas_click4
pending_verificationhas_click4
suspendedhas_click5

Worked solution and explanation

What this really is

This is an existence test over an entire population, dressed up as an engagement breakdown. The skill being probed: can you put every user into exactly one bucket based on whether a row exists somewhere else, without letting the other table decide who counts? Anyone can filter clicked = 1. The trick is that the answer has to be driven from users, because the people you are measuring the silence of are exactly the ones ad_impressions never mentions. Start from the impressions and every never-served user vanishes; join the raw impressions and every heavy clicker is counted once per click.


Building it

Step 1: Build the clicker set as c

Inside the join, a small subquery pulls user_id from ad_impressions where clicked = 1 and dedupes it with DISTINCT. Aliased c, this set has one row per clicker no matter how many ads they clicked, so the join that follows cannot fan out.

Step 2: Anchor on users

LEFT JOIN that set onto users. Every user survives: a clicker gets a matching c.user_id, everyone else (saw ads and ignored them, or never saw one) gets NULL. An inner join here would quietly delete the entire no-click population.

Step 3: Label and count per status

A CASE on c.user_id IS NULL names the bucket, and account_status comes straight from users. Grouping on both gives one row per status and bucket, and COUNT(*) is a user count because each user is exactly one row after step 1.

Every user, one bucket, per status
SELECT
    u.account_status,
    CASE WHEN c.user_id IS NULL THEN 'no_click' ELSE 'has_click' END AS click_group,
    COUNT(*) AS user_count
FROM users AS u
LEFT JOIN (
    SELECT DISTINCT user_id
    FROM ad_impressions
    WHERE clicked = 1
) AS c
    ON c.user_id = u.user_id
GROUP BY u.account_status, click_group

The clicker set c is deduped before the join, so COUNT(*) counts users, not clicks.


Starting from impressions
SELECT clicked, COUNT(DISTINCT user_id) FROM ad_impressions GROUP BY clicked never sees a user with zero impressions, and a user with both a clicked and an unclicked impression shows up in both buckets.
Starting from users
users LEFT JOIN the deduped clicker set keeps every account, and the IS NULL test puts each one in exactly one bucket.

Common follow-up questions