Feature Flag Engagement Impact

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

The growth team wants to understand session activity around feature flag launches. For each feature flag, return the flag name, a display name (underscores replaced with spaces), whether the flag is currently enabled, and how many user sessions started in the 30 days following the flag's created date. Flags with zero qualifying sessions should still appear with a session count of zero. Sort by flag name.

Flags on versus flags off. The engagement gap.

Tables

feat_flags

Columns: flag_id INTEGER, flag_name TEXT, enabled INTEGER, rollout REAL, owner TEXT, created TEXT, updated TEXT

feat_flags: 10 of 200 rows
flag_idflag_nameenabledrolloutownercreatedupdated
131new_checkout0NULLengineering2025-02-022026-02-02
162beta_search0NULLgrowth2025-03-032026-03-03
193ai_recs139platform2025-04-042026-04-04
224live_chat0NULLmobile2025-05-052026-05-05
255video_upload0NULLdata2025-06-062026-06-06
286two_factor178product2025-07-072026-07-07
317bulk_export0NULLengineering2025-08-082026-08-08
348new_dashboard0NULLgrowth2025-09-092026-09-09
379mobile_v2117platform2025-10-102026-01-10
410dark_mode0NULLmobile2025-11-112026-02-11

user_sessions

Columns: session_id INTEGER, user_id INTEGER, device_id INTEGER, session_start TEXT, session_duration_sec INTEGER, pages_viewed INTEGER

user_sessions: 10 of 200 rows
session_iduser_iddevice_idsession_startsession_duration_secpages_viewed
70611972302026-02-02 01:00:00533
71222942592026-03-03 02:00:00766
71833912882026-04-04 03:00:00999
72444883172026-05-05 04:00:0012212
73055853462026-06-06 05:00:0014515
73666823752026-07-07 06:00:0016818
74277794042026-08-08 07:00:0019121
7488876NULL2026-09-09 08:00:0021424
75499734622026-01-10 09:00:0023727
761010704912026-02-11 10:00:0026030

push_notifs

Columns: notif_id INTEGER, user_id INTEGER, title TEXT, platform TEXT, status TEXT, sent_at TEXT, opened INTEGER, campaign TEXT

push_notifs: 10 of 200 rows
notif_iduser_idtitleplatformstatussent_atopenedcampaign
1053100New message from teamandroidpending2026-02-02 01:07:000winback_feb
1106197Weekly digest readywebfailed2026-03-03 02:14:000promo_summer
1159294Price drop alertiOSbounced2026-04-04 03:21:001onboard_v2
1212391Security alertAndroidDelivered2026-05-05 04:28:000NULL
1265488Payment receivedWEBFAILED2026-06-06 05:35:00NULLloyalty_2024
1318585Complete your profilebasicdelivered2026-07-07 06:42:001NULL
1371682New feature availablepremiumpending2026-08-08 07:49:000reengagement
1424779Subscription renewaliosfailed2026-09-09 08:56:000NULL
1477876Flash sale: 50% offandroidbounced2026-01-10 09:03:001flash_sale
1530973Your order shipped!webDelivered2026-02-11 10:10:00NULLretention_q1

Expected output

First 5 of 200 rows
flag_namedisplay_nameenabledlaunch_window_sessions
ai_recsai recs10
beta_searchbeta search07
bulk_exportbulk export00
dark_modedark mode05
live_chatlive chat00

Worked solution and explanation

The mental model

Experimentation teams use this to check one habit: do you treat a date-bounded inequality as a JOIN condition or a WHERE filter? Both compute different answers, and only one preserves flags that had zero qualifying sessions. The interviewer is watching whether you reach for LEFT JOIN ... ON DATE before they have to ask, and whether you say COUNT(us.session_id) instead of COUNT(*) without prompting.


The two traps that will burn you

Step 1: Trap 1: WHERE kills your LEFT JOIN

If you write the window filter in WHERE (WHERE us.session_start >= ff.created AND us.session_start < ...), the LEFT JOIN silently becomes an INNER JOIN. SQL evaluates ON first, producing NULL rows for unmatched flags, then WHERE filters those NULLs out (because NULL fails any comparison). Result: flags with zero sessions vanish from your output and you fail the 'still appear with zero' requirement. Fix: put the window predicate inside ON.

Step 2: Trap 2: COUNT(*) lies on a LEFT JOIN

On a LEFT JOIN, unmatched flags produce one synthetic row with NULL in every right-side column. COUNT(*) counts that synthetic row as 1, so a flag with no sessions reports launch_window_sessions = 1. COUNT(us.session_id) skips the NULL and correctly reports 0. Always count a specific right-side column when LEFT JOIN is involved.


The solution

LEFT JOIN with the window in ON, count the right-side key
SELECT
    ff.flag_name,
    REPLACE(ff.flag_name, '_', ' ') AS display_name,
    ff.enabled,
    COUNT(us.session_id) AS launch_window_sessions
FROM feat_flags ff
LEFT JOIN user_sessions us
    ON DATE(us.session_start) >= DATE(ff.created)
   AND DATE(us.session_start) <  DATE(ff.created, '+30 days')
GROUP BY ff.flag_id, ff.flag_name, ff.enabled
ORDER BY ff.flag_name

Common follow-up questions