Top 2 Ad Campaigns by Spend
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
- Asked in
- General Assembly
Problem
Return the two ad campaigns with the highest total spend, largest first. A media analyst is pulling this from ad_impressions, where each clicked impression records what the advertiser paid in revenue.
Two campaigns. Most of the budget.
Tables
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
| ad_campaign | total_revenue |
|---|---|
| LOYALTY_PROGRAM | 215.35 |
| SUMMER_SALE_2024 | 16.7 |
Worked solution and explanation
What this really is
This is a grain problem dressed up as a marketing leaderboard. ad_impressions holds one row per impression, and revenue is what that single impression earned, filled only when it was clicked. The board lives at the campaign grain, so the real work is collapsing impressions into one total per ad_campaign before anything is sorted or cut. Anyone can write a sort with a cut-off. What separates candidates is where the cut lands: after the totals, or straight on the raw rows. Sort raw rows and you get the two biggest single payments. On this data both are 'LOYALTY_PROGRAM' at 4.50, so one campaign appears twice and 'SUMMER_SALE_2024' never shows up.
Getting there
Step 1: Fix the grain before writing anything
One row per campaign means ad_campaign is the only key you keep. Carry impression_id or impression_time along and a campaign splits into fragments, and the totals stop meaning anything.
Step 2: Total the spend per ad_campaign
SUM(revenue) per campaign. Unclicked impressions hold NULL in revenue, and SUM skips them, which is exactly the business rule: nobody paid for an impression nobody clicked. No COALESCE is needed, because every campaign has at least one paid impression.
Step 3: Sort the totals, then cut to two
Order by total_revenue descending and keep two rows. The board reads 215.35, 16.70, then 6.20 for 'HOLIDAY_PROMO', so the cut lands on a clear gap and LIMIT 2 needs no tiebreaker.
The solution
SELECT
ad_campaign,
SUM(revenue) AS total_revenue
FROM ad_impressions
GROUP BY ad_campaign
ORDER BY total_revenue DESC
LIMIT 2SELECT
ad_campaign,
revenue
FROM ad_impressions
ORDER BY revenue DESC
LIMIT 2Returns two 4.50 payments, both from 'LOYALTY_PROGRAM': individual impressions, not campaign totals.
Common follow-up questions
- Two campaigns now tie for second place. How do you return both? (Moves the cut from `LIMIT` to a tie-aware ranking over the totals.)
- Internal campaigns whose names contain 'test' must be left off. Does that filter go before or after the totals? (Row filter on `ad_campaign` before aggregation versus a filter on aggregated rows.)
- Spend should cover only the current year. What changes, and what does it cost on a table partitioned by `impression_time`? (Date window plus partition pruning.)
- Is `revenue` what the advertiser paid or what the platform kept? How would you confirm before shipping? (Schema-reading discipline on a column whose name and business meaning can drift.)