Lists from Subqueries: IN
In lesson 3, you used IN with a list of values: WHERE id IN (1, 3, 5). The list doesn't have to be typed by hand: a subquery can make it. Which games have at least one five-star review? The IDs of those games are in the table reviews:
SELECT game_id FROM reviews WHERE stars = 5;| game_id |
|---|
| 1 |
| 1 |
| 3 |
| 6 |
| … |
Put this query in parentheses after IN, and the outer query finds the titles of the games:
SELECT title
FROM games
WHERE id IN (SELECT game_id FROM reviews WHERE stars = 5)
ORDER BY title;| title |
|---|
| Cube Logic |
| Dragon Forge |
| Knight's Oath |
| Moon Farm |
| Star Pilots |
| Turbo Karts |
| Zombie Town |
The subquery runs first and returns a list: 1, 1, 3, 6, 7, 10, 14, 15. Then IN checks each game: is its id in the list? Dragon Forge (1) is in the list twice, but that doesn't matter: IN only asks whether the value is there, so each game is in the result at most once.
Unlike the subqueries of the previous exercise, a subquery after IN may return any number of rows, even none (then IN finds nothing). But it must return exactly one column, because IN compares one value with a list of values:
SELECT title
FROM games
WHERE id IN (SELECT game_id, stars FROM reviews);Error on line 1: sub-select returns 2 columns - expected 1You could find the games with a join too:
SELECT g.title
FROM games AS g
JOIN reviews AS r ON r.game_id = g.id
WHERE r.stars = 5
ORDER BY g.title;| title |
|---|
| Cube Logic |
| Dragon Forge |
| Dragon Forge |
| Knight's Oath |
| Moon Farm |
| Star Pilots |
| Turbo Karts |
| Zombie Town |
A join makes one row for every matching review, so Dragon Forge, with two five-star reviews, is there twice, and you'd need DISTINCT. Which one to use? When you need columns of the other table in the result, such as the stars or the comment, you need a join. When the other table only decides which rows to keep, IN with a subquery says exactly what you mean: “the games whose ID is among these”.
Typical mistake: = instead of IN. WHERE id = (SELECT game_id FROM reviews WHERE stars = 5) isn't an error in SQLite, but as you saw in the previous exercise, it takes only the first value of the list, so it finds just Dragon Forge.
Instructions
The query in the editor finds the games that someone has bought, with a join and DISTINCT. Rewrite it without the join: show the titles of the games whose id is in the list of game_id values of order_items. Use IN with a subquery. The result stays the same:
| title |
|---|
| Bakery Story |
| Castle Siege |
| Cube Logic |
| … |
Below it, show the name and the city of the customers who placed an order in December, in any year. The subquery finds the IDs of these customers in orders; the month of a date is strftime('%m', ordered_at). Sort by name:
| name | city |
|---|---|
| Emma Clark | London |
| Noah Wilson | Leeds |
| Olivia Brown | London |
| Oscar King | Bristol |
A subquery can contain another subquery. Show the name and the email of the customers who reviewed Dragon Forge: a subquery finds the IDs of the reviewers in reviews, and inside it, another subquery finds the ID of the game by its title. Sort by name:
| name | |
|---|---|
| Ava Green | ava.green@mail.com |
| Emma Clark | emma.clark@mail.com |
| Leo Walker | leo.walker@mail.com |
| Liam Turner | liam.t@mail.com |
Start programming for free
2/8