Ad Revenue by Age Bucket

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
L3
Asked in
Airbnb

Problem

How much ad revenue does each age bucket bring in, with the impressions' revenue in ad_impressions and each user's bucket in users, from the highest-earning bucket down? Users with no age bucket on file stay out of the totals.

Ad dollars, sliced by country.

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

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

Expected output

First 5 of 6 rows
age_buckettotal_revenue
65+58.6
45-5447.95
55-6438.55
35-4434.5
25-3431.5

Worked solution and explanation

What this is really asking

This is a fact-to-dimension rollup in a demographics costume. The money lives in ad_impressions.revenue and the age bucket lives in users.age_bucket, so neither table can answer the question alone. Everyone remembers to sum. What separates candidates is joining before grouping and sorting on the aggregate rather than the label. Group ad_impressions by user_id and you return per-user totals that never roll up to a demographic. Sort by age_bucket and '18-24' leads only because it comes first as text.

Build it in three moves

Step 1: Join ad_impressions to users on user_id

The bucket exists only on the user row, so every impression has to borrow it. Inner is the right join here: the impressions with a NULL user_id have no bucket to land in, and an inner join drops them without a special case.

Step 2: Drop users with no age_bucket before grouping

Some users have no bucket on file, and several of them hold impressions. Without WHERE u.age_bucket IS NOT NULL, their revenue forms its own blank group, a seventh row nobody asked for. Filtering in WHERE means that group is never built.

Step 3: Sum revenue per bucket and sort on the total

SUM(ai.revenue) skips the roughly half of impressions that earned nothing, so no COALESCE is needed. Sort on total_revenue descending. That value exists only after the aggregate, which is exactly why the sort belongs on it.

Ad revenue by age bucket
SELECT u.age_bucket,
       SUM(ai.revenue) AS total_revenue
FROM ad_impressions ai
INNER JOIN users u
  ON ai.user_id = u.user_id
WHERE u.age_bucket IS NOT NULL
GROUP BY u.age_bucket
ORDER BY total_revenue DESC
Single-table shortcut
SELECT user_id, SUM(revenue) FROM ad_impressions GROUP BY user_id runs cleanly and returns a couple dozen per-user totals. It looks like progress, but age_bucket can never be reached from here.
Join, then roll up
Joining to users first gives every impression a bucket, so GROUP BY u.age_bucket collapses everything into six demographic rows that read top to bottom.

Common follow-up questions

Common follow-up questions