Combining tables with JOIN
Real databases split data across tables: temperature_readings holds raw measurements keyed by station_id, while weather_stations holds the metadata — name, lat, lon. A JOIN stitches them back together on the shared key, one combined row per match.
The code
SELECT s.name, s.lat, s.lon, r.temp_c FROM weather_stations AS s JOIN temperature_readings AS r ON s.station_id = r.station_id ORDER BY r.temp_c DESC LIMIT 5;
Reading it piece by piece:
AS s/AS r— short aliases so you writes.nameinstead ofweather_stations.name.ON s.station_id = r.station_id— the match rule: a row pair survives only when the two IDs agree.- Plain
JOINmeansINNER JOIN— only matches. A reading whosestation_idis missing from the stations table silently drops out (which is also how joins audit broken keys).
In practice
Facts in one table, context in another — a data engineer joins them to build the dataset analysts actually query. Sensor readings + station metadata, orders + customers, events + users: same pattern everywhere. Most production queries you'll write are just JOIN + WHERE + GROUP BY composed together.
Python bridge: df_stations.merge(df_readings, on="station_id"). Pandas' .merge() is exactly an inner JOIN — SQL does it inside the warehouse so you never ship two big tables into Python.
Further reading
SQL INNER JOIN, animated with GIFs — Data School
Try it
Practice question: using these sample tables, write a query that lists name, lat, lon, and temp_c for all readings above 30°C, sorted by temperature descending.
| weather_stations | |||
|---|---|---|---|
| station_id | name | lat | lon |
| 1 | Princeton | 40.36 | -74.66 |
| 2 | Boulder | 40.02 | -105.27 |
| 3 | Houston | 29.76 | -95.37 |
| temperature_readings | ||
|---|---|---|
| station_id | temp_c | recorded_at |
| 1 | 24.5 | 06:00 |
| 2 | 19.2 | 06:00 |
| 3 | 33.8 | 06:00 |
| 1 | 25.1 | 07:00 |
| 3 | 34.2 | 07:00 |
Show answer
SELECT s.name, s.lat, s.lon, r.temp_c FROM weather_stations AS s JOIN temperature_readings AS r ON s.station_id = r.station_id WHERE r.temp_c > 30 ORDER BY r.temp_c DESC;
| name | lat | lon | temp_c |
|---|---|---|---|
| Houston | 29.76 | -95.37 | 34.2 |
| Houston | 29.76 | -95.37 | 33.8 |
Only the two Houston readings clear 30°C — Princeton (25.1) and Boulder (19.2) don't. Note the duplicated Houston row: the join emits one output row per match, so a station with two readings appears twice. This is how one-to-many joins multiply rows, and why you can double-count if you aggregate after joining on a non-unique key.