User Devices
A medium SQL interview practice problem on DataDriven. Write the query and run it against a real database, with instant feedback.
- Domain
- SQL
- Difficulty
- medium
- Seniority
- L3
Problem
Sessions repeat heavily, so a user with fifty phone sessions still counts as engaging on one platform: each username and device type pair belongs in the result exactly once. From users, user_sessions and devices, list every device type each user with at least one session has engaged on, next to their username.
Desktop, mobile, tablet. What does each user actually use?
Tables
users
Columns: user_id INTEGER, username TEXT, email TEXT, signup_date TEXT, account_status TEXT, age_bucket TEXT
| user_id | username | signup_date | account_status | age_bucket | |
|---|---|---|---|---|---|
| 100 | alice | alice@example.com | 2025-02-02 | inactive | 25-34 |
| 197 | aaron42 | aaron42@example.com | 2026-03-03 | suspended | 35-44 |
| 294 | amelia | amelia@example.com | 2024-04-04 | pending_verification | 45-54 |
| 391 | arjun | arjun@example.com | 2025-05-05 | active | 55-64 |
| 488 | ava99 | ava99@example.com | 2026-06-06 | inactive | 65+ |
| 585 | andrew_k | andrew_k@example.com | 2024-07-07 | suspended | NULL |
| 682 | anika | anika@example.com | 2025-08-08 16:34:00 | pending_verification | 18-24 |
| 779 | aiden | aiden@example.com | 2026-09-09 14:53:00 | active | 25-34 |
| 876 | aria_b | aria_b@example.com | 2024-10-10 | inactive | 35-44 |
| 973 | brian | brian@example.com | 2025-11-11 | suspended | 45-54 |
user_sessions
Columns: session_id INTEGER, user_id INTEGER, device_id INTEGER, session_start TEXT, session_duration_sec INTEGER, pages_viewed INTEGER
| session_id | user_id | device_id | session_start | session_duration_sec | pages_viewed |
|---|---|---|---|---|---|
| 7061 | 197 | 230 | 2026-02-02 01:00:00 | 53 | 3 |
| 7122 | 294 | 259 | 2026-03-03 02:00:00 | 76 | 6 |
| 7183 | 391 | 288 | 2026-04-04 03:00:00 | 99 | 9 |
| 7244 | 488 | 317 | 2026-05-05 04:00:00 | 122 | 12 |
| 7305 | 585 | 346 | 2026-06-06 05:00:00 | 145 | 15 |
| 7366 | 682 | 375 | 2026-07-07 06:00:00 | 168 | 18 |
| 7427 | 779 | 404 | 2026-08-08 07:00:00 | 191 | 21 |
| 7488 | 876 | NULL | 2026-09-09 08:00:00 | 214 | 24 |
| 7549 | 973 | 462 | 2026-01-10 09:00:00 | 237 | 27 |
| 7610 | 1070 | 491 | 2026-02-11 10:00:00 | 260 | 30 |
devices
Columns: device_id INTEGER, device_type TEXT, os_name TEXT, os_version TEXT, browser TEXT
| device_id | device_type | os_name | os_version | browser |
|---|---|---|---|---|
| 201 | desktop | Android | 15.1 | Chrome |
| 230 | tablet | Windows | 16.2 | Firefox |
| 259 | smart_tv | macOS | 17.0 | Edge |
| 288 | wearable | Linux | 11 | Opera |
| 317 | mobile | tvOS | 12 | Samsung Internet |
| 346 | desktop | watchOS | 13 | NULL |
| 375 | tablet | iOS | 10.0.19041 | Safari |
| 404 | smart_tv | Android | 14.1 | Chrome |
| 433 | wearable | Windows | NULL | Firefox |
| 462 | mobile | macOS | 14.0 | Edge |
Expected output
| username | device_type |
|---|---|
| aaron42 | tablet |
| amelia | smart_tv |
| arjun | wearable |
| ava99 | mobile |
| andrew_k | desktop |
Worked solution and explanation
What this really is
This is a two-hop lookup through a bridge table that looks like a simple user report. The attribute you need, device_type, does not live on the session fact: user_sessions carries only device_id, so users joined to user_sessions alone can never answer it. Most candidates find the second hop to devices. The real split is what they do with the fan-out. Every session becomes a row, and with roughly 12.5 sessions per user spread over only four device types, the same (user, platform) pair comes back over and over. Skip the dedup and a result of about 60 pairs becomes tens of millions of rows, and any per-user platform count downstream measures session volume instead of platforms.
Getting there
Step 1: Hop from users to user_sessions
Inner-join on user_id. The inner join is the requirement: a user with no sessions has no platform to report, so dropping them is correct, not a side effect.
Step 2: Hop from user_sessions to devices
Join on device_id to reach device_type. This is the hop people skip when they assume the session row knows its platform. It does not, and the query will either fail or invent a column.
Step 3: Collapse the fan-out to one row per pair
Project only u.username and d.device_type, then SELECT DISTINCT over exactly those two columns. Project anything session-level, like session_id or session_start, and the dedup does nothing because every row is unique again.
SELECT DISTINCT u.username, d.device_type
FROM users u
JOIN user_sessions s ON u.user_id = s.user_id
JOIN devices d ON s.device_id = d.device_id- Join without dedup
- One row per session: a user with 100 'mobile' sessions shows 'mobile' 100 times, and the output is as large as
user_sessionsitself. - Join with dedup
DISTINCTon (username,device_type) keeps one row per pair, so the result size tracks users times platforms, not session volume.
Common follow-up questions
- How would you count platforms per user instead of listing them? (Grouping by `u.user_id` with `COUNT(DISTINCT d.device_type)`; grouping by `username` alone breaks if usernames ever repeat.)
- What if users with zero sessions must appear too? (A `LEFT JOIN` chain from `users`, and deciding how a missing `device_type` should be shown.)
- Which users engage on more than one platform? (`HAVING COUNT(DISTINCT d.device_type) > 1` after grouping by user.)