Weekend Warriors
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
- L3
Problem
event_data holds one row per app event, each carrying its event_type and the event_timestamp it happened at. For every event type, show how many of its events fell on a weekend and how many fell on a weekday as two counts side by side, most weekend events first, with types that share a weekend count in any order.
Weekdays vs. weekends. When does the action really happen?
Tables
event_data
Columns: event_id INTEGER, event_type TEXT, user_id INTEGER, event_timestamp TEXT, payload TEXT, tags TEXT, properties TEXT
| event_id | event_type | user_id | event_timestamp | payload | tags | properties |
|---|---|---|---|---|---|---|
| 3059 | button_click | 100 | 2026-02-02 01:07:00 | {"button_id": "cancel", "element_class": "btn-primary", "position": {"x": 13, "y": 7}} | ["desktop", "web", "returning"] | {"os": "Edge", "language": "US", "screen_size": "en", "utm_source": "mobile"} |
| 3118 | purchase | 197 | 2026-03-03 02:14:00 | {"amount": 56.93, "currency": "GBP", "items": [{"sku": "SKU1002", "qty": 3}]} | ["ios", "premium", "new_user", "organic"] | {"country": "Windows", "device_type": "FR", "referrer": "fr", "utm_medium": "1920x1080", "browser": "twitter"} |
| 3177 | signup | 294 | 2026-04-04 03:21:00 | {"signup_method": "apple", "referral_code": "REF3X", "accepted_terms": true} | ["android", "free", "campaign_a", "referral", "experiment_v1"] | {"language": "iOS", "screen_size": "es", "utm_source": "desktop", "session_id": "bing", "os": "organic"} |
| 3236 | login | 391 | 2026-05-05 04:28:00 | {"login_method": "password", "remember_me": true, "mfa_used": false} | ["web", "returning"] | {"device_type": "JP", "referrer": "ja", "utm_medium": "1440x900"} |
| 3295 | logout | 488 | 2026-06-06 05:35:00 | {"action": "logout", "value": 50} | ["premium", "new_user", "organic"] | {"screen_size": "de", "utm_source": "tablet", "session_id": "amazon", "os": "Chrome"} |
| 3354 | search | 585 | 2026-07-07 06:42:00 | {"query": "phone", "results_count": 102, "filters": {"category": "electronics"}} | ["free", "campaign_a", "referral", "experiment_v1"] | {"referrer": "mobile", "utm_medium": "375x812", "browser": "email", "country": "Edge", "device_type": "US"} |
| 3413 | add_to_cart | NULL | 2026-08-08 07:49:00 | {"product_id": 1007, "quantity": 2, "price": 58.49, "variant": "medium"} | ["returning", "campaign_b", "featured", "experiment_v2", "ios"] | {"utm_source": "fr", "session_id": "twitter", "os": "Safari", "language": "Windows", "screen_size": "FR"} |
| 3472 | open | 779 | 2026-09-09 08:56:00 | {"action": "open", "value": 80} | ["new_user", "organic"] | {"utm_medium": "bing", "browser": "organic", "country": "iOS"} |
| 3531 | crash | 876 | 2026-01-10 09:03:00 | {"action": "crash", "value": 90} | ["campaign_a", "referral", "experiment_v1"] | {"session_id": "direct", "os": "Firefox", "language": "macOS", "screen_size": "JP"} |
| 3590 | error | 973 | 2026-02-11 10:10:00 | {"action": "error", "value": 100} | ["campaign_b", "featured", "experiment_v2", "ios"] | {"browser": "Chrome", "country": "Android", "device_type": "DE", "referrer": "de", "utm_medium": "tablet"} |
Expected output
| event_type | weekend_count | weekday_count |
|---|---|---|
| purchase | 10 | 19 |
| signup | 6 | 7 |
| share | 6 | 6 |
| page_view | 6 | 11 |
| error | 6 | 7 |
Worked solution and explanation
What this problem really is
This is a pivot dressed up as product analytics: one table goes in, and each event type comes out as one row carrying two counts side by side. Anyone can count events by type. What separates candidates is splitting that count into a weekend bucket and a weekday bucket on the same row, and reading the weekend correctly off the day-of-week encoding. SQLite's strftime('%w', ...) makes Sunday '0' and Saturday '6'. Assume the days run 1 to 7, or that the weekend is '6' and '7', and Sunday silently lands in the weekday bucket. Every weekend count comes out low, and the weekend-first ordering, the whole point of the question, comes out wrong.
Break down the requirements
Step 1: Classify each event by day of week
strftime('%w', event_timestamp) returns '0' for Sunday and '6' for Saturday. Those two values are the weekend; '1' through '5' are weekdays. Say the mapping out loud before writing it: this is where most wrong answers start.
Step 2: Pivot with conditional sums
SUM(CASE WHEN strftime('%w', event_timestamp) IN ('0', '6') THEN 1 ELSE 0 END) is the weekend count, and the same expression with NOT IN is the weekday count. A SUM over 1s and 0s is a row counter that respects the condition, and both buckets fill in the same pass.
Step 3: Group by event_type
GROUP BY event_type gives one row per type with both counts populated. A type with no weekend events would still appear, with weekend_count at 0, which a weekend-only filter would have dropped.
Step 4: Sort by weekend activity
ORDER BY weekend_count DESC puts the most weekend-driven types first. The grader checks that rows are sorted by the weekend count, so no tiebreaker is owed.
The solution
SELECT event_type,
SUM(CASE WHEN strftime('%w', event_timestamp) IN ('0', '6')
THEN 1 ELSE 0 END) AS weekend_count,
SUM(CASE WHEN strftime('%w', event_timestamp) NOT IN ('0', '6')
THEN 1 ELSE 0 END) AS weekday_count
FROM event_data
GROUP BY event_type
ORDER BY weekend_count DESC- Two filtered counts, joined back
- A weekend CTE with
WHERE ... IN ('0', '6')and a weekday CTE withNOT IN, each grouped byevent_type, then joined onevent_type. It returns the right answer, but it scans 150M rows twice and needs an outer join to keep a type missing from one side. - One pass, condition inside the count
SUM(CASE WHEN ... THEN 1 ELSE 0 END)lets the condition pick the bucket instead of deciding which rows survive. One scan, one hash aggregate, no join to get wrong.
Common follow-up questions
- How would you add a weekend-to-weekday ratio column? (Use `weekend_count * 1.0 / NULLIF(weekday_count, 0)` to avoid integer division and a zero divisor.)
- What if timestamps came in multiple timezones and the `%w` extraction had to use local time? (Store the offset with each row and shift with `datetime(event_timestamp, tz_offset)` before `strftime`, or materialize a local-time column in the load job.)
- How would the plan change at 10B events? (Precompute a daily rollup of `event_type`, `weekend_count` and `weekday_count`, partitioned by date, so the dashboard reads the aggregate instead of the raw events.)