JOIN 连接两张表
订单表里记着每个订单是哪个客户下的,可只记了客户的编号:
1SELECT * FROM orders ORDER BY id;| id | customer_id | order_date | status |
|---|---|---|---|
| 1 | 1 | 2025-09-02 | 已发货 |
| 2 | 2 | 2025-09-05 | 已发货 |
| 3 | 1 | 2025-09-18 | 已发货 |
| 4 | 4 | 2025-09-21 | 已取消 |
| 5 | 5 | 2025-10-01 | 已发货 |
| 6 | 3 | 2025-10-03 | 已发货 |
| 7 | 2 | 2025-10-10 | 待发货 |
| 8 | 7 | 2025-10-12 | 已发货 |
| 9 | 1 | 2025-10-15 | 待发货 |
| 10 | 8 | 2025-10-20 | 已发货 |
| 11 | 5 | 2025-10-22 | 已发货 |
| 12 | 4 | 2025-10-25 | 待发货 |
4 号订单是 customer_id 为 4 的客户下的。4 号客户叫什么、住哪儿?要去客户表里找。这一课学怎么一条查询同时用两张表。
为什么要分好几张表
把客户的名字和城市直接写进每个订单不行吗?行,可是 1 号客户下了 3 单,她的名字、城市就要写 3 遍。哪天她搬家了,3 个地方都得改,漏改一个,数据就自相矛盾了。
所以数据库通常这样存:客户的信息只在客户表里写一次,订单里只写客户的编号。用的时候再按编号把两张表连接起来。
JOIN … ON
FROM 表一 JOIN 表二 ON 条件:把两张表的行一对一对配起来,ON 说怎么配。这里是订单的 customer_id 等于客户的 id:
1SELECT orders.id, orders.order_date, customers.name2FROM orders3JOIN customers ON orders.customer_id = customers.id4ORDER BY orders.id;
| id | order_date | name |
|---|---|---|
| 1 | 2025-09-02 | 林晓 |
| 2 | 2025-09-05 | 王磊 |
| 3 | 2025-09-18 | 林晓 |
| 4 | 2025-09-21 | 张伟 |
| 5 | 2025-10-01 | 刘洋 |
| 6 | 2025-10-03 | 陈静 |
| 7 | 2025-10-10 | 王磊 |
| 8 | 2025-10-12 | 孙强 |
| 9 | 2025-10-15 | 林晓 |
| 10 | 2025-10-20 | 周婷 |
| 11 | 2025-10-22 | 刘洋 |
| 12 | 2025-10-25 | 张伟 |
两张表都有的列名(比如 id)要写成 表名.列名,说清是哪张表的。
点单步看看 JOIN 那一步:每个订单拿着自己的 customer_id 去客户表里找 id 一样的那一行,找到了就拼成一行。12 个订单,拼出 12 行。
JOIN 也可以写成 INNER JOIN,意思完全一样。
表的别名
表名每次都写全太长。在 FROM 和 JOIN 里给表起个短名字,后面就用短名字:
1SELECT o.id, o.order_date, c.name, c.city2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id4ORDER BY o.id;
| id | order_date | name | city |
|---|---|---|---|
| 1 | 2025-09-02 | 林晓 | 上海 |
| 2 | 2025-09-05 | 王磊 | 北京 |
| 3 | 2025-09-18 | 林晓 | 上海 |
| 4 | 2025-09-21 | 张伟 | 广州 |
| 5 | 2025-10-01 | 刘洋 | 成都 |
| 6 | 2025-10-03 | 陈静 | 上海 |
| 7 | 2025-10-10 | 王磊 | 北京 |
| 8 | 2025-10-12 | 孙强 | 广州 |
| 9 | 2025-10-15 | 林晓 | 上海 |
| 10 | 2025-10-20 | 周婷 | 上海 |
| 11 | 2025-10-22 | 刘洋 | 成都 |
| 12 | 2025-10-25 | 张伟 | 广州 |
orders AS o 的意思是「orders 表,下面叫它 o」。AS 也可以省掉,写成 orders o,意思一样。
同名的列
只在一张表里有的列,比如 order_date、name,可以不写前面的表名。可两张表都有的列不行:
1SELECT id, name, order_date2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id;
ERROR 1052 (23000): Column 'id' in field list is ambiguous
「id」在好几张表里都有,要写成 表名.id,说清楚是哪一张的。
ERROR 1052:id 这一列「不明确」。订单表有 id,客户表也有 id,数据库不知道你要哪个。写成 o.id 或 c.id 就好。
小技巧: 连接查询里,每一列都写上表的别名(o.id、c.name),哪怕不写也不会报错。读的人一眼就知道每一列从哪儿来。
配不上的行没了
反过来,从客户这边连接订单:
1SELECT c.id, c.name, o.id AS order_id2FROM customers AS c3JOIN orders AS o ON o.customer_id = c.id4ORDER BY c.id, o.id;
| id | name | order_id |
|---|---|---|
| 1 | 林晓 | 1 |
| 1 | 林晓 | 3 |
| 1 | 林晓 | 9 |
| 2 | 王磊 | 2 |
| 2 | 王磊 | 7 |
| 3 | 陈静 | 6 |
| 4 | 张伟 | 4 |
| 4 | 张伟 | 12 |
| 5 | 刘洋 | 5 |
| 5 | 刘洋 | 11 |
| 7 | 孙强 | 8 |
| 8 | 周婷 | 10 |
再点单步看看配对:1 号客户配上了 3 个订单,变成 3 行;6、9、10 号客户在订单表里一个订单都找不到,配不上,就没了,结果里根本没有他们。
JOIN 只留下两边都配上的。要把没下过单的客户也留下,得用第 12 课的 LEFT JOIN。
商品表有 10 行,订单明细表有 18 行,台灯(6 号)从来没人买过。结果有几行?
1SELECT p.name, oi.order_id2FROM products AS p3JOIN order_items AS oi ON oi.product_id = p.id4ORDER BY oi.order_id, p.id;
每一行明细都能找到它的商品,所以 18 行明细都配上了,拼出 18 行。台灯配不上任何明细,不在结果里;有的商品出现在好几个订单里,就出现好几次。
JOIN 加上 WHERE
连接好的结果和一张普通的表一样,后面照样可以写 WHERE、ORDER BY。上海的客户下过哪些订单?
1SELECT o.id, o.order_date, c.name2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id4WHERE c.city = '上海'5ORDER BY o.id;
| id | order_date | name |
|---|---|---|
| 1 | 2025-09-02 | 林晓 |
| 3 | 2025-09-18 | 林晓 |
| 6 | 2025-10-03 | 陈静 |
| 9 | 2025-10-15 | 林晓 |
| 10 | 2025-10-20 | 周婷 |
city 在客户表里,order_date 在订单表里,连接以后一行里两样都有,WHERE 可以用任何一张表的列。
订单里买了什么
订单明细表只有商品编号。连接商品表,就能看到 3 号订单买的是什么:
1SELECT p.name, oi.quantity, oi.unit_price2FROM order_items AS oi3JOIN products AS p ON oi.product_id = p.id4WHERE oi.order_id = 35ORDER BY p.id;
| name | quantity | unit_price |
|---|---|---|
| 耳机 | 1 | 59.00 |
| 数据线 | 2 | 8.50 |
三张表一起连接(订单 → 明细 → 商品),第 13 课再讲。
练一练
写好查询,点检查。提示随便看,实在想不出来再看答案。
待发货的订单
✓查出所有待发货的订单:订单的 id、order_date,和下单客户的 name,按订单 id 排序。
| id | order_date | name |
|---|---|---|
| 7 | 2025-10-10 | 王磊 |
| 9 | 2025-10-15 | 林晓 |
| 12 | 2025-10-25 | 张伟 |
1SELECT id, order_date, customer_id2FROM orders3WHERE status = '待发货';
卖出去的数码产品
✓从订单明细里查出所有数码类商品的那几行:order_id、商品的 name、quantity,按 order_id 排,同一个订单里按商品编号排。
| order_id | name | quantity |
|---|---|---|
| 3 | 耳机 | 1 |
| 3 | 数据线 | 2 |
| 4 | 手机壳 | 1 |
| 8 | 耳机 | 1 |
| 10 | 数据线 | 1 |
| 10 | 手机壳 | 1 |
1SELECT order_id, product_id, quantity FROM order_items;修好它:同名的列
✓想查出广州客户下的订单:订单的 id、客户的 name、order_date,按订单 id 排序,却报了 ERROR 1052。改好它。
| id | name | order_date |
|---|---|---|
| 4 | 张伟 | 2025-09-21 |
| 8 | 孙强 | 2025-10-12 |
| 12 | 张伟 | 2025-10-25 |
1SELECT id, name, order_date2FROM orders AS o3JOIN customers AS c ON o.customer_id = c.id4WHERE city = '广州'5ORDER BY id;
这一课学会了
- 数据分在几张表里,每样信息只写一次,表之间用编号联系。
FROM 表一 JOIN 表二 ON 条件把两边配得上的行拼成一行;JOIN就是INNER JOIN。- 给表起别名(
orders AS o),列写成o.id;两张表都有的列不写表名会得到ERROR 1052。 - 配不上的行不会出现在结果里;要留下它们,用第 12 课的
LEFT JOIN。 - 连接后的结果照样可以用
WHERE、ORDER BY。