The Crowded Hour

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
L5
Asked in
Amazon

Problem

We size capacity per device type to its worst-case crowding, not its average, and this quarter there is only budget to reinforce the three most strained types. For each type, find the busiest rolling 45-day window, the stretch that saw the most different people active on it, and report those three types with that peak headcount and the window it covers, most crowded first.

Every device type has a moment when the room is fullest. Find it.

Tables

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

devices

Columns: device_id INTEGER, device_type TEXT, os_name TEXT, os_version TEXT, browser TEXT

devices: 10 of 200 rows
device_iddevice_typeos_nameos_versionbrowser
201desktopAndroid15.1Chrome
230tabletWindows16.2Firefox
259smart_tvmacOS17.0Edge
288wearableLinux11Opera
317mobiletvOS12Samsung Internet
346desktopwatchOS13NULL
375tabletiOS10.0.19041Safari
404smart_tvAndroid14.1Chrome
433wearableWindowsNULLFirefox
462mobilemacOS14.0Edge

Expected output

First 3 of 3 rows
device_typewindow_spanpeak_active_users
smart_tv2026-02-27 to 2026-04-137
tablet2026-05-04 to 2026-06-187
desktop2026-02-11 to 2026-03-286

Worked solution and explanation

What this really is

Strip off the capacity-planning costume and this is an interval-overlap self-join. The question is not how many people used each device type in total; it is how crowded the busiest rolling stretch ever got, then which types get crowded hardest. A plain GROUP BY session_start cannot see that: two sessions starting days apart still share a rolling window, and grouping by a timestamp counts each row alone. The skill is comparing every session against every other of the SAME device type, counting the DISTINCT users caught in each window, keeping the single fullest window per type, then surfacing only the three most strained. Miss the DISTINCT and one regular's repeat visits inflate the crowd; bucket by calendar month and you report the wrong peak; keep the orphan sessions and a NULL device type sneaks into the ranking.


Break down the requirements

Step 1: Attach the device type, dropping the orphans

Inner-join user_sessions to devices on device_id to attach device_type. The inner join is deliberate: a session with a null device_id, or one absent from devices, has no type, so it drops instead of polluting a bucket. Park the result in a device_sessions CTE for the self-join.

Step 2: Count the distinct crowd inside each rolling window

Self-join device_sessions on the SAME device_type, keeping the other session only when its session_start falls in the anchor's forward window (b.session_start >= a.session_start AND b.session_start < datetime(a.session_start, '+45 days')), and take COUNT(DISTINCT b.user_id) as the crowd. The distinct is load-bearing: a person with several sessions is one body, not several; dropping it pushes smart_tv from 7 to 8 and reorders the top types. This per-anchor rolling window is exactly what a GROUP BY on session_start cannot express.

Step 3: Keep the single fullest window per device type

ROW_NUMBER() OVER (PARTITION BY device_type ORDER BY active_users DESC, window_start ASC) numbers each type's windows fullest-first, breaking a reach tie by the earliest window. Keep window_rank = 1 and you have the single peak window per device type.

Step 4: Order by the peak, take the top three

ORDER BY peak_active_users DESC, device_type ASC then LIMIT 3, since only three types get reinforced this quarter. Build the label with substr(window_start, 1, 10) || ' to ' || date(window_start, '+45 days'). The second sort key makes the order total, so the two types tied at 7 (smart_tv, tablet) land predictably, and the LIMIT deliberately cuts the two calmest (mobile, wearable); drop it and you report all five.


The solution

Three most crowded device types by peak rolling 45-day crowd
WITH device_sessions AS (
    SELECT us.session_id, us.user_id, us.session_start, d.device_type
    FROM user_sessions us
    INNER JOIN devices d ON us.device_id = d.device_id
),
window_reach AS (
    SELECT a.device_type,
           a.session_start AS window_start,
           COUNT(DISTINCT b.user_id) AS active_users
    FROM device_sessions a
    INNER JOIN device_sessions b
      ON b.device_type = a.device_type
     AND b.session_start >= a.session_start
     AND b.session_start < datetime(a.session_start, '+45 days')
    GROUP BY a.device_type, a.session_id, a.session_start
),
ranked AS (
    SELECT device_type,
           substr(window_start, 1, 10) || ' to ' || date(window_start, '+45 days') AS window_span,
           active_users,
           ROW_NUMBER() OVER (
               PARTITION BY device_type
               ORDER BY active_users DESC, window_start ASC
           ) AS window_rank
    FROM window_reach
)
SELECT device_type, window_span, active_users AS peak_active_users
FROM ranked
WHERE window_rank = 1
ORDER BY peak_active_users DESC, device_type ASC
LIMIT 3
Calendar-month GROUP BY
Grouping by strftime('%Y-%m', session_start) snaps every crowd to a month boundary. A crowd peaking across the last week of one month and the first three of the next is split in half and never seen. The reported peak is an artifact of where the calendar happens to cut.
Rolling-window self-join
Anchoring a 45-day window at every session lets it land wherever the crowd actually is. The self-join gives each anchor its own window, which no GROUP BY on a bare timestamp can reproduce.

Common follow-up questions