Joins compose with everything you know
A join's output is just rows, so every tool from units 2 through 4 works on it. Filter joined rows with WHERE:
SELECT customers.name, orders.item FROM orders JOIN customers ON customers.id = orders.customer_id WHERE customers.city = 'Tokyo' ORDER BY orders.id;
The join runs first in your mental model (build the combined rows), then WHERE keeps only the rows whose customer lives in Tokyo. Note the power move here: you filtered orders using a column that only exists in customers.
Code exercise · sql
Run it. Only Ben lives in Tokyo, so only his two orders survive the filter.
Join, then group
GROUP BY works on joined rows too. Total spend per customer, by name this time instead of by a repeated name column (compare lesson 4-2):
SELECT customers.name, SUM(orders.price) AS total FROM orders JOIN customers ON customers.id = orders.customer_id GROUP BY customers.name ORDER BY customers.name;
Build the joined rows, bucket them by name, sum each bucket. This join + GROUP BY combination is probably the single most common query shape in real applications: revenue per product, posts per user, students per course.
Code exercise · sql
Run it. One row per customer with their total: Ana 5+4, Ben 8+5, Cara 5.
Code exercise · sql
Your turn. How many orders come from each CITY? Print city and count, alphabetical by city. Lima gets Ana's 2, Tokyo gets Ben's 2, Paris gets Cara's 1.
Quiz
You run the city count query but accidentally write GROUP BY customers.name while selecting customers.city and COUNT(*). Why is that wrong in principle?