Point of Entry

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
Meta

Problem

The growth team is checking whether the people who arrive and simply look around ever turn into buyers. For every user whose very first recorded event was a page view, count how many purchases they later made, and keep the ones who never bought with a count of zero. Order the result from the most purchases down, breaking ties by user ID ascending.

Everyone starts by looking. Count who came back to buy.

Tables

event_data

Columns: event_id INTEGER, event_type TEXT, user_id INTEGER, event_timestamp TEXT, payload TEXT, tags TEXT, properties TEXT

event_data: 10 of 200 rows
event_idevent_typeuser_idevent_timestamppayloadtagsproperties
3059button_click1002026-02-02 01:07:00{"button_id": "cancel", "element_class": "btn-primary", "position": {"x": 13, "y": 7}}["desktop", "web", "returning"]{"os": "Edge", "language": "US", "screen_size": "en", "utm_source": "mobile"}
3118purchase1972026-03-03 02:14:00{"amount": 56.93, "currency": "GBP", "items": [{"sku": "SKU1002", "qty": 3}]}["ios", "premium", "new_user", "organic"]{"country": "Windows", "device_type": "FR", "referrer": "fr", "utm_medium": "1920x1080", "browser": "twitter"}
3177signup2942026-04-04 03:21:00{"signup_method": "apple", "referral_code": "REF3X", "accepted_terms": true}["android", "free", "campaign_a", "referral", "experiment_v1"]{"language": "iOS", "screen_size": "es", "utm_source": "desktop", "session_id": "bing", "os": "organic"}
3236login3912026-05-05 04:28:00{"login_method": "password", "remember_me": true, "mfa_used": false}["web", "returning"]{"device_type": "JP", "referrer": "ja", "utm_medium": "1440x900"}
3295logout4882026-06-06 05:35:00{"action": "logout", "value": 50}["premium", "new_user", "organic"]{"screen_size": "de", "utm_source": "tablet", "session_id": "amazon", "os": "Chrome"}
3354search5852026-07-07 06:42:00{"query": "phone", "results_count": 102, "filters": {"category": "electronics"}}["free", "campaign_a", "referral", "experiment_v1"]{"referrer": "mobile", "utm_medium": "375x812", "browser": "email", "country": "Edge", "device_type": "US"}
3413add_to_cartNULL2026-08-08 07:49:00{"product_id": 1007, "quantity": 2, "price": 58.49, "variant": "medium"}["returning", "campaign_b", "featured", "experiment_v2", "ios"]{"utm_source": "fr", "session_id": "twitter", "os": "Safari", "language": "Windows", "screen_size": "FR"}
3472open7792026-09-09 08:56:00{"action": "open", "value": 80}["new_user", "organic"]{"utm_medium": "bing", "browser": "organic", "country": "iOS"}
3531crash8762026-01-10 09:03:00{"action": "crash", "value": 90}["campaign_a", "referral", "experiment_v1"]{"session_id": "direct", "os": "Firefox", "language": "macOS", "screen_size": "JP"}
3590error9732026-02-11 10:10:00{"action": "error", "value": 100}["campaign_b", "featured", "experiment_v2", "ios"]{"browser": "Chrome", "country": "Android", "device_type": "DE", "referrer": "de", "utm_medium": "tablet"}

Expected output

First 5 of 6 rows
user_idpurchase_count
121285
120314
119343
118372
117401

Worked solution and explanation

What this really is

Strip the activity-tracking costume and this is a cohort definition stacked on a conditional count. You carve out a population by each user's FIRST event, then count a DIFFERENT event type for exactly those users. The trap lives in four words of the prompt: 'with a count of zero'. Define the cohort, switch to an inner join to count purchases, and every viewer who never bought silently disappears from your result. The grader is counting those exact people, so an inner join quietly deletes the rows you were asked to keep.


Break down the requirements

Step 1: Define the cohort by first event type

Use a CTE with ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_timestamp) and filter to rn = 1 AND event_type = 'page_view'. This is the earliest event of ANY type, then a check that it was a page view, not 'the first page_view in their history'.

Step 2: Count purchase events for the cohort

Left join the cohort users back to event_data filtered to event_type = 'purchase' and count the matched event rows per user.

Step 3: Keep the zero-purchase viewers

Because it is a LEFT JOIN, a viewer with no purchase rows still produces one output row, and COUNT over the null join partner returns 0 instead of dropping them. Swap in an inner join here and those users vanish.


The solution

Cohort definition by first event then conditional count
WITH first_events AS (
    SELECT user_id, event_type,
        ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_timestamp) AS rn
    FROM event_data
),
viewer_cohort AS (
    SELECT user_id
    FROM first_events
    WHERE rn = 1 AND event_type = 'page_view'
)
SELECT
    vc.user_id,
    COUNT(e.event_id) AS purchase_count
FROM viewer_cohort vc
LEFT JOIN event_data e
    ON vc.user_id = e.user_id AND e.event_type = 'purchase'
GROUP BY vc.user_id
ORDER BY purchase_count DESC, vc.user_id ASC

Common follow-up questions