数据拆到多张 Table 后,我们经常要把它们重新组合。orders 只有 user_id 和 product_id,但展示订单时,人们想看用户姓名和商品名称,而不是数字 ID。SQL 用 JOIN 连接相关 Row。
下面三张表就是本节 JOIN 使用的源数据。先用 orders.user_id 找到用户,再用 orders.product_id 找到商品。
| users.id | users.name |
|---|---|
| 1 | Alice |
| 2 | Bob |
| 3 | Chris |
| 4 | Diana |
| orders.id | orders.user_id | orders.product_id | orders.quantity |
|---|---|---|---|
| 1 | 1 | 2 | 1 |
| 2 | 1 | 3 | 2 |
| 3 | 2 | 1 | 1 |
| 4 | 3 | 4 | 1 |
| products.id | products.name |
|---|---|
| 1 | Keyboard |
| 2 | Mouse |
| 3 | Coffee Mug |
| 4 | Backpack |
因此订单 1 的两个外键 user_id = 1、product_id = 2 会解析为一条可读结果:Alice · Mouse · 1。
INNER JOIN:只留下匹配
INNER JOIN 只返回两边能成功匹配的数据。条件 ON orders.user_id = users.id 告诉数据库:订单的 user_id 等于某位用户的 id 时,把两条 Row 拼在一起。Alice 有两个订单,所以会出现两次;JOIN 不会合并用户,它组合的是相关 Row。
SELECT orders.id, users.name, orders.quantity
FROM orders
INNER JOIN users
ON orders.user_id = users.id;一次连接三张表
从 orders 开始,通过 user_id 找到用户,再通过 product_id 找到商品,最后选择要展示的 Column。不同表经常都有 id 和 name,所以多表查询通常写 users.name、products.name 和 orders.id,避免歧义。
别名让长查询更易读:o 表示 orders,u 表示 users,p 表示 products。定义后便使用 o.id、u.name 和 p.name。
SELECT o.id, u.name AS user_name,
p.name AS product_name, o.quantity
FROM orders AS o
INNER JOIN users AS u ON o.user_id = u.id
INNER JOIN products AS p ON o.product_id = p.id;LEFT JOIN:保留左边全部数据
如果要“列出所有用户,即使没有订单”,从 users 出发使用 LEFT JOIN。Alice、Bob 和 Chris 会匹配订单;没有订单的 Diana 仍出现,但右表的 order_id 用 NULL 填充。“左边”就是 FROM 后的 Table。
SELECT u.name, o.id AS order_id
FROM users AS u
LEFT JOIN orders AS o
ON u.id = o.user_id;RIGHT JOIN:保留右边全部数据
RIGHT JOIN 与 LEFT JOIN 镜像:右边 Table 的 Row 全部保留,没有左侧匹配时左侧 Column 为 NULL。部分 SQL 产品不支持 RIGHT JOIN,但交换 Table 顺序后使用 LEFT JOIN 可以表达同样逻辑。
选择 JOIN 时,先用自然语言说清谁必须全部出现:只看成功配对用 INNER;保留 FROM 后的全部 Row 用 LEFT;保留 JOIN 后的全部 Row 用 RIGHT。
JOIN 可以继续搭配 WHERE 和 ORDER BY。 JOIN 负责组合,WHERE 负责筛选组合后的 Row,ORDER BY 负责排序。
INNER 与 LEFT:哪些 Row 会留下?
users 在左,orders 在右。切换 JOIN,观察 Diana 与 NULL。
| user | order_id | quantity |
|---|---|---|
| Alice | 1 | 1 |
| Alice | 2 | 2 |
| Bob | 3 | 1 |
| Chris | 4 | 1 |
INNER JOIN 只保留两侧成功匹配的 Row。RIGHT JOIN:保证商品清单完整
orders 在左,products 在右。切换 INNER 与 RIGHT;观察从未卖出的 Notebook。
| order_id | product | quantity |
|---|---|---|
| 3 | Keyboard | 1 |
| 1 | Mouse | 1 |
| 2 | Coffee Mug | 2 |
| 4 | Backpack | 1 |
INNER JOIN 只保留两侧成功匹配的 Row。知识测验
知识测验
知识测验
生成可读订单清单
电商客服需要 order_id、user_name、product_name 和 quantity。连接 orders、users、products,使用 o、u、p 别名,并只返回 Alice 的订单。
找出尚未下单的用户
运营团队要联系从未下单的用户。写一个 LEFT JOIN,保留所有 users,并通过 WHERE o.id IS NULL 只留下无匹配订单的人。解释为什么不能使用 INNER JOIN。