Top-Ranked Wines by Variety

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

The product team is looking at content that runs shorter than user 197's typical session. Take user 197's average session length, then return every content item whose duration is at or below that average, walking through them from the lowest content ID up to the highest. For each row, show the literal user ID 197, the content ID, and the content's duration in seconds.

The best bottles. Ranked by variety.

Tables

content_items

Columns: content_id INTEGER, title TEXT, content_type TEXT, duration_seconds INTEGER, creator_id INTEGER, publish_date TEXT

content_items: 10 of 200 rows
content_idtitlecontent_typeduration_secondscreator_idpublish_date
243The Ultimate Guide to AI TrendsarticleNULL1972026-02-02
286Top 10 Mobile Appspodcast1542942025-03-03
329Understanding Machine Learning [DRAFT]short2013912026-04-04
372Mastering Tech Startupslivestream2484882025-05-05
415Introduction to Cybersecurityvideo2955852026-06-06
458Deep Dive: Cloud ComputingarticleNULL6822025-07-07
501Behind the Scenes of Product Managementpodcast3897792026-08-08
544Breaking Down DevOpsshort4368762025-09-09
587Exploring Web Developmentlivestream483NULL2026-01-10
630How to Data Sciencevideo53010702025-11-11

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

Expected output

First 5 of 30 rows
user_idcontent_idduration_seconds
197286154
197329201
197372248
197415295
197501389

Worked solution and explanation

One number, two tables that never meet

This is a threshold lookup dressed up as a product question. The skill being probed: can you compute one number from user_sessions and use it to filter content_items, a table it has no relationship to? Anyone can write AVG(...). The trap is reaching for a JOIN because two tables are on the screen. They share no key, so the join either fails or fans out, and each content item gets compared once per session instead of once against the average.

No edge between the tables: nothing links a content item to a session.
  • content_items: content_id PK, title, content_type, duration_seconds, creator_id, publish_date
  • user_sessions: session_id PK, user_id, device_id, session_start, session_duration_sec, pages_viewed

Step 1: Compute the threshold as a scalar subquery

SELECT AVG(session_duration_sec) FROM user_sessions WHERE user_id = 197 returns exactly one row and one column. That is what lets it sit on the right side of a comparison. Drop the WHERE and you get the global average, a plausible number that quietly returns the wrong set of content.

Step 2: Filter content_items against it

Put the subquery inside WHERE ci.duration_seconds <= (...). The statement says "at or below", so it is <=, not <. The engine evaluates the uncorrelated subquery once, then scans content_items once.

Step 3: Emit 197 as a literal

user_id does not exist on content_items, and you are not joining to get it. 197 AS user_id stamps the constant on every row, which is exactly what the output asks for.

Step 4: Order by content_id

"Lowest ID up to the highest" is ORDER BY ci.content_id, ascending. The comparison is order sensitive, so this is not optional.

Scalar subquery as the cutoff
SELECT 197 AS user_id, ci.content_id, ci.duration_seconds
FROM content_items ci
WHERE ci.duration_seconds <= (
  SELECT AVG(session_duration_sec) FROM user_sessions WHERE user_id = 197
)
ORDER BY ci.content_id

One pass over user_sessions for the average, one over content_items for the filter.

The join instinct
A cross join of content_items and user_sessions pairs every content row with every session of user 197. A content item can match several sessions and come back several times, and the cutoff becomes each individual session instead of the average.
The scalar subquery
The subquery collapses user 197's sessions into one number first. Each content row is compared against that single value and appears at most once.
The wrong version
SELECT 197 AS user_id, ci.content_id, ci.duration_seconds
FROM content_items ci
CROSS JOIN user_sessions us
WHERE us.user_id = 197
  AND ci.duration_seconds <= us.session_duration_sec
ORDER BY ci.content_id

Duplicates content items and compares against single sessions, not the average.

Common follow-up questions