Top Earner Per Campaign

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

Domain
SQL
Difficulty
medium
Seniority
L4
Asked in
General Assembly

Problem

Our campaign recap names exactly one top earner per campaign, even when several users brought in the same top revenue. Rebuild it from ad_impressions with every top earner and their total revenue, leaving out impressions with no user attached, campaigns in alphabetical order and tied earners by user id.

The top-earning user per campaign.

Tables

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 9 rows
ad_campaignuser_idtotal_revenue
BRAND_AWARENESS_Q14883.6
FLASH_SALE_48H4885.6
HOLIDAY_PROMO9734.35
LOYALTY_PROGRAM291322.5
LOYALTY_PROGRAM320422.5

Worked solution and explanation

This is a top-with-ties problem dressed up as a campaign leaderboard. Everyone writes the GROUP BY ad_campaign, user_id and the SUM(revenue). What separates candidates is what they do when two users land on the same total. On this data the loyalty campaign has two users at exactly 22.5, so the right answer is 9 rows for 8 campaigns. Reach for a one-winner pick and you return 8, quietly crowning one of them. Nobody notices, because 8 rows for 8 campaigns looks exactly right.

Three moves, in this order

Step 1: Total each user inside each campaign

Drop rows where user_id is NULL first. Those impressions belong to nobody, and if you leave them in they form a phantom user who competes for the top. Then GROUP BY ad_campaign, user_id with SUM(revenue). The grain is the whole game: rank impressions instead of totals and the winner is whoever had the single biggest click, not the biggest earner.

Step 2: Rank the totals per campaign, keeping ties

RANK() OVER (PARTITION BY ad_campaign ORDER BY SUM(revenue) DESC) is evaluated after the GROUP BY, so it can sort by the aggregate directly. RANK gives equal totals the same number, so both 22.5 users get 1. Without the PARTITION BY you get one company-wide winner instead of one per campaign.

Step 3: Keep rank 1, then sort the output

A window result cannot be filtered in the WHERE of the query that computes it, so filter rnk = 1 one level out. Then sort by ad_campaign and user_id, which puts tied users side by side in a predictable order.

Sum revenue per campaign and user, then keep everyone at rank 1
SELECT ad_campaign, user_id, total_revenue
FROM (
    SELECT
        ad_campaign,
        user_id,
        SUM(revenue) AS total_revenue,
        RANK() OVER (PARTITION BY ad_campaign ORDER BY SUM(revenue) DESC) AS rnk
    FROM ad_impressions
    WHERE user_id IS NOT NULL
    GROUP BY ad_campaign, user_id
) ranked
WHERE rnk = 1
ORDER BY ad_campaign, user_id
One winner per campaign
ROW_NUMBER() filtered to 1, or a per-campaign ORDER BY ... LIMIT 1, always yields exactly one row per campaign. Here that is 8 rows, and which tied user survives depends on the order the engine happened to read.
Every tied winner
RANK() filtered to 1 keeps every user whose total equals the campaign maximum: 9 rows. DENSE_RANK() behaves identically at rank 1; the two only differ further down.
The version that looks right
SELECT ad_campaign, user_id, total_revenue
FROM (
    SELECT
        ad_campaign,
        user_id,
        SUM(revenue) AS total_revenue,
        ROW_NUMBER() OVER (PARTITION BY ad_campaign ORDER BY SUM(revenue) DESC) AS rn
    FROM ad_impressions
    WHERE user_id IS NOT NULL
    GROUP BY ad_campaign, user_id
) ranked
WHERE rn = 1
ORDER BY ad_campaign, user_id

Same shape, 8 rows: ROW_NUMBER breaks the tie arbitrarily and drops a top earner.

Common follow-up questions