SQL Problem Solving: Reading Complex Queries
Concept. Read a query in execution order, not written order: FROM, then WHERE, then GROUP BY, then HAVING, then SELECT, then ORDER BY. Each clause hands its rows to the next.
Intuition. To see what a six-clause query returns, trace it one clause at a time over the Listens rows, watching the row set shrink and reshape at each step.
Example 1: Finding Top Genres Per User
-- Goal: For each user, find genres they have 2+ songs in.
-- Read in execution order: FROM/JOIN → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
SELECT user_id, genre, -- Step 5: project columns
COUNT(*) AS song_count,
AVG(rating) AS avg_rating
FROM Listens l -- Step 1a: read Listens
JOIN Songs s ON l.song_id = s.song_id -- Step 1b: join Songs
WHERE genre IS NOT NULL -- Step 2: drop NULL genres
GROUP BY user_id, genre -- Step 3: form groups
HAVING COUNT(*) >= 2 -- Step 4: keep 2+ songs
ORDER BY user_id, avg_rating DESC; -- Step 6: sort output
The Mental Model
Example 2: Finding Power Users
The Query
-- Goal: Find the top-half users by average rating.
-- Three layers: per-user metrics (CTE 1), percentile rank (CTE 2), final filter.
WITH user_stats AS ( -- CTE 1: per-user metrics
SELECT user_id,
COUNT(*) AS listen_count,
AVG(rating) AS avg_rating
FROM Listens
GROUP BY user_id
),
ranked_users AS ( -- CTE 2: percentile rank
SELECT user_id, listen_count, avg_rating,
PERCENT_RANK() OVER (ORDER BY avg_rating DESC) AS rating_rank
FROM user_stats
)
SELECT u.name, -- final layer: name + filter
r.listen_count,
r.avg_rating
FROM ranked_users r
JOIN Users u ON r.user_id = u.user_id
WHERE r.rating_rank < 0.5 -- top half
ORDER BY r.avg_rating DESC;
How to Read This Query
<img src="svgs/reading-trace-2.svg" alt="Example 2 traced on one page. CTE 1 builds per-user metrics (count and average rating). CTE 2 adds a percentile rank over average rating descending. The outer WHERE rating_rank < 0.5 strikes Mickey (0.5) and Daffy (1.0), leaving only Minnie at rank 0.0, average 4.4." style="width:100%; max-width:1180px; display:block; margin:1.5rem auto;">