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

Count the purchases of every visitor in event_data whose very first recorded event was a page view, keeping the ones who never bought at zero. Events logged without a user ID all come from one anonymous visitor, judged by the same first-event rule. The list shows whether people who start by just looking around ever turn into buyers, so it runs from the most purchases down.

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 defined by each user's earliest row, stacked on a count of a different event type that has to keep its zeros. There is a trap in each half. Filter event_type = 'page_view' and you get everyone who ever viewed a page, not everyone whose first event was a view, so users who signed up or clicked first leak into the cohort. Then count purchases through an inner join and every viewer who never bought silently disappears: the look-but-never-buy segment the question exists to measure.


Break down the requirements

Step 1: Define the cohort by first event type

In the first_events CTE, number every event per user by event_timestamp, then keep rn = 1 AND event_type = 'page_view' in viewer_cohort. The earliest event of any type is chosen first and only then checked, which is what 'very first recorded event' means.

Step 2: Count purchase events for the cohort

Join viewer_cohort back to event_data on user_id, with e.event_type = 'purchase' inside the ON clause, and count the matched e.event_id per user. Every purchase counts, whenever it happened.

Step 3: Keep the zero-purchase viewers

Because it is a LEFT JOIN, a viewer with no purchase rows still produces one row with a null e.event_id, and COUNT(e.event_id) scores it 0. COUNT(*) would score that same row 1, and an inner join would drop it entirely.


The solution

Cohort by first event, then a count that keeps zeros
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

Common follow-up questions