65a48fa5ae
Fourth cast: build a minimal two-table schema with rows, switch to advanced mode (`mode advanced`), and run a join pairing each book with its author — shows the mode switch + SQL + multi-table result, motion that complements the guide's static examples. Convert the guide to .mdx and embed above the intro. Recorded via `pnpm casts`; build clean (25 pages).
135 lines
5.8 KiB
Plaintext
135 lines
5.8 KiB
Plaintext
---
|
|
title: Querying with joins
|
|
description: Combine rows from related tables — author with book, member with loan — using SQL joins in advanced mode.
|
|
sidebar:
|
|
order: 2
|
|
---
|
|
|
|
import Demo from '../../../components/Demo.astro';
|
|
|
|
[Build the library](/guides/build-the-library/) left you with four related
|
|
tables. So far each `show data` looks at one table at a time — but the
|
|
interesting questions span tables: *who wrote this book? who has borrowed it?*
|
|
Answering those means a **join**: matching rows from one table against related
|
|
rows in another.
|
|
|
|
<Demo src="/casts/joins.cast" title="Switch to advanced mode, then join books to their authors." />
|
|
|
|
Joins are part of SQL, so this guide is in **advanced mode**. Switch to it with
|
|
the `mode` command:
|
|
|
|
```rdbms
|
|
mode advanced
|
|
```
|
|
|
|
If you only need one SQL command without leaving simple mode, prefix it with a
|
|
colon instead — see [Simple and advanced modes](/getting-started/modes/).
|
|
|
|
## Join two tables
|
|
|
|
`books` stores an `author_id`, not the author's name. To show the name, join
|
|
`books` to `authors`, matching each book's `author_id` to the author it points
|
|
at:
|
|
|
|
```sql
|
|
select authors.name, books.title from books
|
|
join authors on books.author_id = authors.author_id
|
|
order by authors.name
|
|
```
|
|
|
|
```
|
|
┌───────────────────┬───────────────────────────┐
|
|
│ name │ title │
|
|
├───────────────────┼───────────────────────────┤
|
|
│ Italo Calvino │ Invisible Cities │
|
|
│ Octavia E. Butler │ Kindred │
|
|
│ Ursula K. Le Guin │ A Wizard of Earthsea │
|
|
│ Ursula K. Le Guin │ The Left Hand of Darkness │
|
|
└───────────────────┴───────────────────────────┘
|
|
```
|
|
|
|
The `on books.author_id = authors.author_id` clause is the heart of the join:
|
|
it says *which* rows belong together. Because Le Guin wrote two books, her name
|
|
appears on two rows — exactly the one-to-many relationship you declared,
|
|
expanded into result rows.
|
|
|
|
## Join through the bridge table
|
|
|
|
The real payoff is the many-to-many link. *Who has borrowed which book?* lives
|
|
in three tables: the borrower in `members`, the book in `books`, and the
|
|
connection in `loans`. Join all three, hopping through the `loans` bridge:
|
|
|
|
```sql
|
|
select members.name, books.title, loans.loaned_on, loans.returned_on from loans
|
|
join books on loans.book_id = books.book_id
|
|
join members on loans.member_id = members.member_id
|
|
order by loans.loaned_on
|
|
```
|
|
|
|
```
|
|
┌──────────────┬──────────────────────┬────────────┬─────────────┐
|
|
│ name │ title │ loaned_on │ returned_on │
|
|
├──────────────┼──────────────────────┼────────────┼─────────────┤
|
|
│ Grace Hopper │ A Wizard of Earthsea │ 2024-05-01 │ (null) │
|
|
│ Alan Turing │ Invisible Cities │ 2024-05-03 │ 2024-05-20 │
|
|
└──────────────┴──────────────────────┴────────────┴─────────────┘
|
|
```
|
|
|
|
Each loan row pulls in the matching book *and* the matching member, turning
|
|
three tables of IDs into a readable sentence: who borrowed what, and when.
|
|
|
|
## Filter a join
|
|
|
|
A `where` clause narrows a join just like it narrows a single table. A loan
|
|
that has not been returned has an empty `returned_on`, so *which books are
|
|
currently out?* is:
|
|
|
|
```sql
|
|
select members.name, books.title from loans
|
|
join books on loans.book_id = books.book_id
|
|
join members on loans.member_id = members.member_id
|
|
where loans.returned_on is null
|
|
```
|
|
|
|
```
|
|
┌──────────────┬──────────────────────┐
|
|
│ name │ title │
|
|
├──────────────┼──────────────────────┤
|
|
│ Grace Hopper │ A Wizard of Earthsea │
|
|
└──────────────┴──────────────────────┘
|
|
```
|
|
|
|
## Summarise with group by
|
|
|
|
Joins combine with `group by` to count and total. *How many books has each
|
|
author written?* groups the joined rows by author and counts each group:
|
|
|
|
```sql
|
|
select authors.name, count(*) as book_count from books
|
|
join authors on books.author_id = authors.author_id
|
|
group by authors.name
|
|
order by book_count desc
|
|
```
|
|
|
|
```
|
|
┌───────────────────┬────────────┐
|
|
│ name │ book_count │
|
|
├───────────────────┼────────────┤
|
|
│ Ursula K. Le Guin │ 2 │
|
|
│ Octavia E. Butler │ 1 │
|
|
│ Italo Calvino │ 1 │
|
|
└───────────────────┴────────────┘
|
|
```
|
|
|
|
`count(*)` counts the rows in each group; `as book_count` names the result
|
|
column. Le Guin's two books collapse into a single row with a count of `2`.
|
|
|
|
## Where to go next
|
|
|
|
- See **how** a query runs — prefix any of these with `explain` to view the
|
|
plan, and add an [index](/reference/indexes/) to speed up a join.
|
|
- The full `select` surface — projections, more join forms, set operations,
|
|
and CTEs — is in
|
|
[Querying & inspecting](/reference/querying-and-inspecting/).
|
|
- Switch back to simple mode any time by running `mode simple`.
|