Campaign Bookend Engagement

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
L5
Asked in
General Assembly

Problem

Each row of ad_impressions is one impression stamped to the second, and every campaign's traffic runs from a first calendar day to a last one. Alphabetically by campaign, what percentage of each campaign's impressions fell on that first day and what percentage on that last day, with the fraction kept rather than cut to a whole number?

First impression versus last. The gap.

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 8 rows
ad_campaignfirst_day_pctlast_day_pct
BRAND_AWARENESS_Q15.88235294117647111.764705882352942
FLASH_SALE_48H6.6666666666666676.666666666666667
HOLIDAY_PROMO6.256.25
LOYALTY_PROGRAM1.19047619047619051.1904761904761905
SUMMER_SALE_20244.7619047619047624.761904761904762

Worked solution and explanation

Why this problem exists in real interviews

This is a per-group boundary comparison dressed up as campaign pacing. Every row has to be measured against its own campaign's first and last calendar day, and neither the row nor a single GROUP BY can see both at once. Anyone can write MIN(impression_time). The trap is comparing against that raw timestamp: it matches one instant, not the whole day, so a campaign whose last day carries two impressions reports half its true share. The quieter trap is types: SUM and COUNT(*) are both integers, so the division silently truncates 5.88 to 5 unless the count is cast first.


Break down the requirements

Step 1: Fix each campaign's bookend days

First CTE campaign_dates: MIN(DATE(impression_time)) and MAX(DATE(impression_time)) grouped by ad_campaign. Truncating inside the aggregate is the whole point: the bookend is a calendar day, not the first second of traffic.

Step 2: Compute each denominator once

Second CTE campaign_counts: COUNT(*) grouped by ad_campaign. Rows with a missing user_id or revenue are still impressions, so count rows, not a column.

Step 3: Count both bookends in the same pass

Join the fact rows back to both CTEs and let SUM(CASE WHEN DATE(a.impression_time) = cd.first_day THEN 1 ELSE 0 END) count the first day while its twin counts the last day, in the same aggregate.

Step 4: Cast the count before dividing

100 * CAST(SUM(...) AS REAL) / cc.total_impressions. Both the sum and total_impressions are integers, so without the cast every share comes back as a whole number.


The solution

Bookend percentages per campaign
WITH campaign_dates AS (
  SELECT ad_campaign,
         MIN(DATE(impression_time)) AS first_day,
         MAX(DATE(impression_time)) AS last_day
  FROM ad_impressions
  GROUP BY ad_campaign
),
campaign_counts AS (
  SELECT ad_campaign, COUNT(*) AS total_impressions
  FROM ad_impressions
  GROUP BY ad_campaign
)
SELECT a.ad_campaign,
       100 * CAST(SUM(CASE WHEN DATE(a.impression_time) = cd.first_day THEN 1 ELSE 0 END) AS REAL)
         / cc.total_impressions AS first_day_pct,
       100 * CAST(SUM(CASE WHEN DATE(a.impression_time) = cd.last_day THEN 1 ELSE 0 END) AS REAL)
         / cc.total_impressions AS last_day_pct
FROM ad_impressions a
JOIN campaign_dates cd ON a.ad_campaign = cd.ad_campaign
JOIN campaign_counts cc ON a.ad_campaign = cc.ad_campaign
GROUP BY a.ad_campaign
ORDER BY a.ad_campaign

Common follow-up questions

Common follow-up questions