N023-H2 Tier 2 · Core SQL · hard analytics · Streamhub

Return any user IDs that have a conversion record but no corresponding session

Part of UNION, UNION ALL, INTERSECT, EXCEPT in SQL

The problem

Streamhub's data quality team is checking referential integrity. Every conversion should belong to a user who has at least one session record on the platform.

Write a query to return any user IDs that have a conversion record but no corresponding session.

Assumptions:

  • Streamhub's referential integrity is currently strong — every user with conversions also has at least one session record. The result set will be empty.
  • The query order matters: conversions EXCEPT sessions answers this question; sessions EXCEPT conversions answers a different one (sessions with no conversions).

Output:

  • One row per orphan conversion-user, with a single column user_id. The result set will be empty.
Schema · analytics 5 tables
users
id integer
name text
email text
country text
plan text
signed_up_at timestamptz
is_active boolean
conversions
id integer
user_id integer
converted_at timestamptz
plan text
amount numeric
sessions
id integer
user_id integer
started_at timestamptz
ended_at? timestamptz
event_count integer
events
id integer
user_id integer
session_id? integer
event_type text
occurred_at timestamptz
properties? jsonb
periods
id integer
name text
start_month integer
end_month integer

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution Try it yourself first
Solution query
SELECT
  user_id
FROM
  conversions
EXCEPT
SELECT
  user_id
FROM
  sessions

The shape

EXCEPT returns rows from the left query that don't appear in the right query. With conversions on the left and sessions on the right, the result is the set of user IDs with a conversion but no session — the orphan-conversion list. Every converting user also has at least one session, so the difference is empty and the integrity check passes cleanly.

Clause by clause

  • SELECT user_id FROM conversions is the left input. Every conversion record projects its user. The data quality question — "are any of these users missing from sessions?" — is asking whether this set is fully contained in the session set.
  • EXCEPT is the set operator. PostgreSQL evaluates both queries, deduplicates each side, then returns rows from the left with no match on the right. When every converting user has at least one session, the difference is empty.
  • SELECT user_id FROM sessions is the right input — the reference set the conversions should be a subset of.
  • The empty result is the affirmative answer: zero orphan conversion-users, the integrity check is clean.

Why this and not the swapped order

Directionality is everything. conversions EXCEPT sessions answers "conversions with no session" — the orphan-conversion check the prompt asks for. sessions EXCEPT conversions answers "sessions with no conversion" — a much larger result catching every user who visited but never paid. Both queries run cleanly; the only difference is which question the human is answering.

The invariant being checked is "every conversion must trace back to a real session." The left query is the side being checked (conversions); the right query is the reference set it should be a subset of (sessions).

The trap

The trap is treating an empty result as a query bug. When an integrity check returns zero rows, the first reaction is to assume the query is misshapen — that EXCEPT is silently filtering something, or that column names don't line up. Neither is the case. The empty result is the affirmative finding: nothing to fix.

The way to verify the query is real is to flip the direction in a separate run. sessions EXCEPT conversions against the same data returns a large non-empty result, which confirms the operator is doing its job and the integrity direction simply happens to be clean.

You practiced EXCEPT as a referential-integrity check. The recurring shape any time the question is "are there entities in A with no counterpart in B" — and the empty-result outcome is a clean integrity pass, not a failure to write the query.

How you actually get good at SQL

Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.

That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

Real problems. Not toy examples.

615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

Write a query. Know if it's right in one second.

No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

Stop guessing whether you actually know it.

SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

Learn it once. Keep it for good.

Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.

Practice, feedback, mastery, review. That's the loop that turns reading into real skill.

Start free

No account, no credit card. Start solving in under a minute.