Detect sessions and gaps using SQL LEAD
Company: Apple
Role: Data Scientist
Category: Data Manipulation (SQL/Python)
Difficulty: medium
Interview Round: Technical Screen
Write a single ANSI-SQL query that (a) assigns per-user session_ids when the gap between consecutive events exceeds 30 minutes, (b) computes session_start, session_end, event_count, session_length_seconds, and next_session_gap_seconds (time from this session_end to the next session_start for the same user), and (c) uses window functions including LEAD at least once.
Schema:
- events(user_id INT, ts TIMESTAMP, action VARCHAR)
Sample data:
+---------+---------------------+---------+
| user_id | ts | action |
+---------+---------------------+---------+
| 1 | 2025-08-01 09:00:00 | view |
| 1 | 2025-08-01 09:10:00 | click |
| 1 | 2025-08-01 10:00:00 | view |
| 2 | 2025-08-01 12:00:00 | view |
| 2 | 2025-08-01 12:20:00 | click |
| 2 | 2025-08-01 12:45:00 | purchase|
| 2 | 2025-08-01 14:00:00 | view |
+---------+---------------------+---------+
Requirements:
- Use LAG to detect new sessions and a running SUM to form session_ids per user.
- Use LEAD(ts) to compute next_event_ts and derive next_session_gap_seconds.
- Return columns: user_id, session_id, session_start, session_end, event_count, session_length_seconds, next_session_gap_seconds (NULL if no next session).
Overview: This question evaluates proficiency in SQL time-series and event-stream manipulation, focusing on window-function techniques for sessionization, timestamp arithmetic, and aggregation to produce session-level metrics, targeting Data Manipulation (SQL/Python) competencies relevant to data scientist roles.
Using PostgreSQL, assign each user's events to sessions. A new session begins only when the gap from the preceding event is greater than 30 minutes; a gap of exactly 30 minutes stays in the same session. Return user_id, a 1-based session_id, session_start, session_end, event_count, session_length_seconds, and next_session_gap_seconds (the seconds from this session's final event to the next session's first event, or NULL for the user's last session). Use LAG to identify session boundaries, a running SUM to form session IDs, and LEAD(ts) to identify the next event. Order by user_id and session_id.
Tables
events(user_id INTEGER, ts TIMESTAMP, action TEXT)
Hints
- Compute previous and next timestamps at event grain before assigning sessions.
- A strict gap greater than 30 minutes starts a new session; exactly 30 minutes does not.
Community answers
Answer by stilllearning
-- Write your SQL query here
with base as (
SELECT user_id
,action
,ts
,lag(ts) over (partition by user_id) as prev_event_ts
,lead(ts) over (partition by user_id) as next_event_ts
FROM events
)
,session_counter as (
SELECT
*,
CASE
WHEN ts::timestamp - prev_event_ts::timestamp > INTERVAL '30 minutes' THEN 1
ELSE 0
END AS session_marker
FROM base
)
,sessions as (
select *
, sum(session_marker) over (partition by user_id order by ts) session_id
from session_counter
)
select user_id
,session_id+1 session_id
,MIN(ts) AS session_start
,MAX(ts) AS session_end
,count(session_id) as event_count
,extract(epoch from MAX(ts) -MIN(ts) ) as session_length_seconds
,MAX(
CASE
WHEN next_event_ts - ts > INTERVAL '30 minutes'
THEN EXTRACT(EPOCH FROM next_event_ts - ts)
END
) AS next_session_gap_seconds
from sessions
group by user_id, session_id