🎯 本月只剩几天支持 SQLTest.online,帮助我们让 SQL 练习对每个人都免费。 今天捐赠 →
SQL 代码已复制到剪贴板

SQL INNER JOIN:合并相互匹配的行

第 5.2 课 · 阅读时间:约 6 分钟 · 练习题 ↓

SQL INNER JOIN 把两张表的行组合起来,只返回连接条件为真的行对,也就是在两张表中都有匹配的行。在本课中,你将学习 INNER JOIN 的语法、表别名的作用、如何连接三张表,以及没有匹配的行会怎样。

在上一课中,你了解了为什么数据会分散在多张表中,以及键如何把它们联系起来。INNER JOIN 是把数据重新组合起来最常用的方式:城市和它所属的国家、付款和对应的客户、演员和他参演的电影。

SQL INNER JOIN:两张表的行按连接条件配对,只有在两张表中都有匹配的行才会出现在结果中

INNER JOIN 返回什么

INNER JOIN 对两张表的行对检查连接条件,只保留条件为真的行对。

  • 第一张表中在第二张表里没有匹配行的行,不会出现在结果中。
  • 第二张表中没有匹配的行同样不会出现。
  • 有多个匹配的行会出现多次,每个匹配一次。

因此,INNER JOIN 常被描述为两张表的交集。

基本语法

SELECT table1.column1, table2.column2
FROM table1
INNER JOIN table2 ON table1.key_column = table2.key_column;
  • INNER JOIN 指定第二张表。
  • ON 设置连接条件,通常是“外键 = 主键”。
  • 关键字 INNER 是可选的:单独写 JOIN 就表示 INNER JOIN。

连接两张表

city 表中存有 country_id,它引用 country 表。要在每个城市旁边显示国家名称:

SELECT city.city, country.country
FROM city
INNER JOIN country ON city.country_id = country.country_id
ORDER BY country.country, city.city;

结果:600 个城市,每个城市都带有所属国家的名称。

当两张表中有同名的列(例如这里的 country_id)时,必须在列名前加上表名,否则数据库会报告列名有歧义。


表别名

较长的表名会让连接查询难以阅读。表别名是在表名后定义的简短名称:

SELECT ci.city, co.country
FROM city AS ci
INNER JOIN country AS co ON ci.country_id = co.country_id
ORDER BY co.country, ci.city;

结果相同。一旦为表指定了别名,就在整个查询中使用该别名。请选择简短但有意义的别名:ci 和 co 比 a 和 b 更容易理解。


连接三张表

一位演员可以出演许多电影,一部电影也有许多演员。这种多对多关系存储在关联表中:film_actor 保存 (actor_id, film_id) 对。要列出演员及其电影片名,需要连接这三张表:

SELECT a.first_name, a.last_name, f.title
FROM actor AS a
INNER JOIN film_actor AS fa ON fa.actor_id = a.actor_id
INNER JOIN film AS f ON f.film_id = fa.film_id
ORDER BY a.last_name, a.first_name, f.title
LIMIT 10;

结果:按演员字母顺序排列的前 10 个“演员-电影”对。

  1. 第一个连接为每位演员附加其在 film_actor 中的行。
  2. 第二个连接为这些行附加对应的电影。
  3. 每个 JOIN 都基于前一个连接的结果工作,因此可以按需连接任意多张表。

JOIN 与 WHERE 和 ORDER BY 组合

连接之后可以继续使用熟悉的子句。例如,查询 Nick Wahlberg 出演的电影:

SELECT f.title, f.release_year, f.rating
FROM actor AS a
INNER JOIN film_actor AS fa ON fa.actor_id = a.actor_id
INNER JOIN film AS f ON f.film_id = fa.film_id
WHERE a.first_name = 'NICK'
  AND a.last_name = 'WAHLBERG'
ORDER BY f.title;

结果:这位演员出演的所有电影的片名、上映年份和分级。


没有匹配的行会消失

INNER JOIN 会悄悄丢弃没有匹配的行。Sakila 中有 1000 部电影,但只有 958 部在 inventory 表中有拷贝:

SELECT f.title, i.inventory_id, i.store_id
FROM film AS f
INNER JOIN inventory AS i ON i.film_id = f.film_id
ORDER BY f.title
LIMIT 10;

结果:电影及其拷贝;42 部没有拷贝的电影根本不会出现,而有多个拷贝的电影会出现多次。

如果没有匹配的行也必须保留在结果中,请使用 LEFT JOIN,你将在下一课中学习它。


要点:

  • INNER JOIN 只返回满足 ON 条件的行对。
  • 在另一张表中没有匹配的行会被丢弃;有多个匹配的行会重复出现。
  • JOIN 和 INNER JOIN 是一样的。
  • 使用表别名,并为两张表中都存在的列加上表前缀。
  • 串联多个 JOIN 来经过 film_actor 这样的关联表。

常见问题

JOIN 和 INNER JOIN 有什么区别?

没有区别。在所有主流数据库中,不指定类型的 JOIN 就是 INNER JOIN。当查询中混用不同类型的连接时,显式写出 INNER JOIN 能让意图更清晰。

为什么我的 INNER JOIN 返回重复的行?

因为某一行在另一张表中有多个匹配。一部在 inventory 中有四个拷贝的电影,在连接 film 和 inventory 后会出现四次。这是连接的正常行为;如果每部电影只需要一行,请进行聚合或过滤。

SQL 中可以连接两张以上的表吗?

可以。每张表添加一个 JOIN ... ON ...。每个连接都把一张新表连接到已经连接好的表上。

尝试解决以下任务,以巩固您在本课中学到的内容。

  1. 经典电影
  2. 使用 JOIN 查找地址
  3. 查找电影中的所有演员
  4. 演员的电影
  5. 查找租赁电影的客户