cswork / libraryOpen practice workspace

Coding questions · eBay

Write SQL with window functions for analytics

This question evaluates understanding and practical use of SQL window functions—specifically rolling 7-day aggregates, deterministic chronological ranking, and retrieving the last non-null value per partition—while also probing awareness of framing, ordering, and performance trade-offs like indexing and partitioning.

Given a table events(user_id, event_time, event_type, value), write SQL to: a) compute each user's rolling 7-day sum of value by day, b) rank each user's events chronologically with deterministic tie-breaking, and c) return, for every event, the last non-null value seen so far per user. Use window functions (PARTITION BY/ORDER BY, ROWS BETWEEN, RANK, LAST_VALUE with appropriate framing). Explain any indexing or partitioning choices for performance.

Rolling 7-day sum of event values per user and day

You are given a table events(user_id, event_time, event_type, value) that stores user activity. Write a SQL query to compute, for each user and each calendar date on which they have at least one event, both:

  1. the total value for that day, and
  2. a rolling 7-day sum of value that includes that day and the previous 6 days, based on daily totals.

Treat NULL in value as 0 when summing. Use window functions with PARTITION BY user_id, ORDER BY event_date, and a ROWS BETWEEN frame applied over daily totals. Also state what index you would recommend on events to make this query efficient on large datasets.

Table events

columntypeconstraints
user_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(20)NOT NULL
valueDECIMAL(10,2)

Sample data:

event_timeevent_typeuser_idvalue
2025-05-01 10:00:00click15
2025-05-03 12:00:00purchase120
2025-05-05 09:00:00click1NULL
2025-05-06 18:30:00purchase110
2025-05-10 14:15:00click18
2025-05-02 08:00:00click2NULL
2025-05-04 16:45:00purchase215
2025-05-04 16:45:00refund2-5
2025-05-07 11:20:00click23
2025-05-09 19:00:00purchase27

Expected output

For each (user_id, event_date), return the daily sum of value (treating NULL as 0) and the rolling 7-row window sum over that user's daily totals, ordered by date.

daily_valueevent_daterolling_7d_valueuser_id
52025-05-0151
202025-05-03251
02025-05-05251
102025-05-06351
82025-05-10431
02025-05-0202
102025-05-04102
32025-05-07132
72025-05-09202

Hints

  1. First aggregate events to daily totals per user in a CTE or subquery, then apply a window function to those daily rows.
  2. Use ROWS BETWEEN 6 PRECEDING AND CURRENT ROW over the ordered event_date to get a 7-day rolling window.

Deterministic chronological ranking of user events

Using the same events(user_id, event_time, event_type, value) table, write a SQL query to rank each user's events in strict chronological order. Use a window function to assign a rank per user that increases by 1 for each successive event. When multiple events for the same user have the same event_time, break ties deterministically by ordering on event_type and then value. Use RANK (or an equivalent ranking window function) with PARTITION BY/ORDER BY. Also mention what index you would recommend to support this query efficiently. Return event_time formatted as YYYY-MM-DD HH24:MI:SS in the sample output.

Table events

columntypeconstraints
user_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(20)NOT NULL
valueDECIMAL(10,2)

Sample data:

event_timeevent_typeuser_idvalue
2025-05-01 10:00:00click15
2025-05-03 12:00:00purchase120
2025-05-05 09:00:00click1NULL
2025-05-06 18:30:00purchase110
2025-05-10 14:15:00click18
2025-05-02 08:00:00click2NULL
2025-05-04 16:45:00purchase215
2025-05-04 16:45:00refund2-5
2025-05-07 11:20:00click23
2025-05-09 19:00:00purchase27

Expected output

Return each event with a per-user chronological rank, breaking ties on event_time by event_type and then value, so ordering is deterministic.

event_rankevent_timeevent_typeuser_idvalue
12025-05-01 10:00:00click15
22025-05-03 12:00:00purchase120
32025-05-05 09:00:00click1NULL
42025-05-06 18:30:00purchase110
52025-05-10 14:15:00click18
12025-05-02 08:00:00click2NULL
22025-05-04 16:45:00purchase215
32025-05-04 16:45:00refund2-5
42025-05-07 11:20:00click23
52025-05-09 19:00:00purchase27

Hints

  1. Use a ranking window function PARTITION BY user_id and ORDER BY event_time.
  2. Add event_type and value to the ORDER BY clause so that rows with the same timestamp are ordered deterministically.

Last non-null value seen so far per user

Using the same events(user_id, event_time, event_type, value) table, write a SQL query that returns, for every event row, the last non-null value seen so far for that user in chronological order. If a user has not yet had any non-null value, the result should be NULL for those early rows. Use a window function with LAST_VALUE (or an equivalent construct) over a frame that starts at the first event for the user and ends at the current row, and make sure null values do not overwrite the last non-null value. Order events per user chronologically, breaking ties on event_time using event_type and value. Also mention what index you would recommend for efficient execution. Return event_time formatted as YYYY-MM-DD HH24:MI:SS in the sample output.

Table events

columntypeconstraints
user_idINTNOT NULL
event_timeTIMESTAMPNOT NULL
event_typeVARCHAR(20)NOT NULL
valueDECIMAL(10,2)

Sample data:

event_timeevent_typeuser_idvalue
2025-05-01 10:00:00click15
2025-05-03 12:00:00purchase120
2025-05-05 09:00:00click1NULL
2025-05-06 18:30:00purchase110
2025-05-10 14:15:00click18
2025-05-02 08:00:00click2NULL
2025-05-04 16:45:00purchase215
2025-05-04 16:45:00refund2-5
2025-05-07 11:20:00click23
2025-05-09 19:00:00purchase27

Expected output

For each event row, return the most recent non-null value for that user up to and including that event, ordered chronologically per user.

event_timeevent_typelast_non_null_valueuser_idvalue
2025-05-01 10:00:00click515
2025-05-03 12:00:00purchase20120
2025-05-05 09:00:00click201NULL
2025-05-06 18:30:00purchase10110
2025-05-10 14:15:00click818
2025-05-02 08:00:00clickNULL2NULL
2025-05-04 16:45:00purchase15215
2025-05-04 16:45:00refund-52-5
2025-05-07 11:20:00click323
2025-05-09 19:00:00purchase727

Hints

  1. Use LAST_VALUE as a window function over a frame from UNBOUNDED PRECEDING to CURRENT ROW so it only looks at rows up to the current one.
  2. If your SQL dialect supports it, use the IGNORE NULLS modifier with LAST_VALUE so that null values do not overwrite the previous non-null value.